]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
#ifdef -DSLAP_NVALUES
[openldap] / servers / slapd / back-bdb / modify.c
1 /* modify.c - bdb backend modify routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 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 int bdb_modify_internal(
18         Operation *op,
19         DB_TXN *tid,
20         Modifications *modlist,
21         Entry *e,
22         const char **text,
23         char *textbuf,
24         size_t textlen )
25 {
26         int rc, err;
27         Modification    *mod;
28         Modifications   *ml;
29         Attribute       *save_attrs;
30         Attribute       *ap;
31
32 #ifdef NEW_LOGGING
33         LDAP_LOG ( OPERATION, ENTRY, "bdb_modify_internal: 0x%08lx: %s\n", 
34                 e->e_id, e->e_dn, 0 );
35 #else
36         Debug( LDAP_DEBUG_TRACE, "bdb_modify_internal: 0x%08lx: %s\n",
37                 e->e_id, e->e_dn, 0);
38 #endif
39
40         if ( !acl_check_modlist( 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 #ifdef NEW_LOGGING
53                         LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify_internal: add\n", 0,0,0);
54 #else
55                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: add\n", 0, 0, 0);
56 #endif
57                         err = modify_add_values( e, mod, get_permissiveModify(op),
58                                 text, textbuf, textlen );
59                         if( err != LDAP_SUCCESS ) {
60 #ifdef NEW_LOGGING
61                                 LDAP_LOG ( OPERATION, ERR, 
62                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
63 #else
64                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
65                                         err, *text, 0);
66 #endif
67                         }
68                         break;
69
70                 case LDAP_MOD_DELETE:
71 #ifdef NEW_LOGGING
72                         LDAP_LOG ( OPERATION, DETAIL1, 
73                                 "bdb_modify_internal: delete\n", 0, 0, 0 );
74 #else
75                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: delete\n", 0, 0, 0);
76 #endif
77                         err = modify_delete_values( e, mod, get_permissiveModify(op),
78                                 text, textbuf, textlen );
79                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
80                         if( err != LDAP_SUCCESS ) {
81 #ifdef NEW_LOGGING
82                                 LDAP_LOG ( OPERATION, ERR, 
83                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
84 #else
85                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
86                                         err, *text, 0);
87 #endif
88                         }
89                         break;
90
91                 case LDAP_MOD_REPLACE:
92 #ifdef NEW_LOGGING
93                         LDAP_LOG ( OPERATION, DETAIL1, 
94                                 "bdb_modify_internal: replace\n", 0, 0, 0 );
95 #else
96                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: replace\n", 0, 0, 0);
97 #endif
98                         err = modify_replace_values( e, mod, get_permissiveModify(op),
99                                 text, textbuf, textlen );
100                         if( err != LDAP_SUCCESS ) {
101 #ifdef NEW_LOGGING
102                                 LDAP_LOG ( OPERATION, ERR, 
103                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
104 #else
105                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
106                                         err, *text, 0);
107 #endif
108                         }
109                         break;
110
111                 case SLAP_MOD_SOFTADD:
112 #ifdef NEW_LOGGING
113                         LDAP_LOG ( OPERATION, DETAIL1, 
114                                 "bdb_modify_internal: softadd\n",0,0,0 );
115 #else
116                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: softadd\n", 0, 0, 0);
117 #endif
118                         /* Avoid problems in index_add_mods()
119                          * We need to add index if necessary.
120                          */
121                         mod->sm_op = LDAP_MOD_ADD;
122
123                         err = modify_add_values( e, mod, get_permissiveModify(op),
124                                 text, textbuf, textlen );
125                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
126                                 err = LDAP_SUCCESS;
127                         }
128
129                         if( err != LDAP_SUCCESS ) {
130 #ifdef NEW_LOGGING
131                                 LDAP_LOG ( OPERATION, ERR, 
132                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
133 #else
134                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
135                                         err, *text, 0);
136 #endif
137                         }
138                         break;
139
140                 default:
141 #ifdef NEW_LOGGING
142                                 LDAP_LOG ( OPERATION, ERR, 
143                                         "bdb_modify_internal: invalid op %d\n", mod->sm_op, 0, 0 );
144 #else
145                         Debug(LDAP_DEBUG_ANY, "bdb_modify_internal: invalid op %d\n",
146                                 mod->sm_op, 0, 0);
147 #endif
148                         *text = "Invalid modify operation";
149                         err = LDAP_OTHER;
150 #ifdef NEW_LOGGING
151                                 LDAP_LOG ( OPERATION, ERR, 
152                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
153 #else
154                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
155                                 err, *text, 0);
156 #endif
157                 }
158
159                 if ( err != LDAP_SUCCESS ) {
160                         attrs_free( e->e_attrs );
161                         e->e_attrs = save_attrs;
162                         /* unlock entry, delete from cache */
163                         return err; 
164                 }
165
166                 /* If objectClass was modified, reset the flags */
167                 if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
168                         e->e_ocflags = 0;
169                 }
170
171                 /* check if modified attribute was indexed
172                  * but not in case of NOOP... */
173                 err = bdb_index_is_indexed( op->o_bd, mod->sm_desc );
174                 if ( err == LDAP_SUCCESS && !op->o_noop ) {
175                         ap = attr_find( save_attrs, mod->sm_desc );
176                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
177
178                         ap = attr_find( e->e_attrs, mod->sm_desc );
179                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
180                 }
181         }
182
183         /* check that the entry still obeys the schema */
184         rc = entry_schema_check( op->o_bd, e, save_attrs, text, textbuf, textlen );
185         if ( rc != LDAP_SUCCESS || op->o_noop ) {
186                 attrs_free( e->e_attrs );
187                 e->e_attrs = save_attrs;
188
189                 if ( rc != LDAP_SUCCESS ) {
190 #ifdef NEW_LOGGING
191                         LDAP_LOG ( OPERATION, ERR, "bdb_modify_internal: "
192                                 "entry failed schema check %s\n", 
193                                 *text, 0, 0 );
194 #else
195                         Debug( LDAP_DEBUG_ANY,
196                                 "entry failed schema check: %s\n",
197                                 *text, 0, 0 );
198 #endif
199                 }
200
201                 /* if NOOP then silently revert to saved attrs */
202                 return rc;
203         }
204
205         /* update the indices of the modified attributes */
206
207         /* start with deleting the old index entries */
208         for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
209                 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
210                         rc = bdb_index_values( op->o_bd, tid, ap->a_desc,
211                                 ap->a_nvals,
212                                 e->e_id, SLAP_INDEX_DELETE_OP );
213                         if ( rc != LDAP_SUCCESS ) {
214                                 attrs_free( e->e_attrs );
215                                 e->e_attrs = save_attrs;
216 #ifdef NEW_LOGGING
217                                 LDAP_LOG ( OPERATION, ERR, 
218                                         "bdb_modify_internal: attribute index delete failure\n",
219                                         0, 0, 0 );
220 #else
221                                 Debug( LDAP_DEBUG_ANY,
222                                        "Attribute index delete failure",
223                                        0, 0, 0 );
224 #endif
225                                 return rc;
226                         }
227                         ap->a_flags &= ~SLAP_ATTR_IXDEL;
228                 }
229         }
230
231         /* add the new index entries */
232         for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
233                 if (ap->a_flags & SLAP_ATTR_IXADD) {
234                         rc = bdb_index_values( op->o_bd, tid, ap->a_desc,
235                                 ap->a_nvals,
236                                 e->e_id, SLAP_INDEX_ADD_OP );
237                         if ( rc != LDAP_SUCCESS ) {
238                                 attrs_free( e->e_attrs );
239                                 e->e_attrs = save_attrs;
240 #ifdef NEW_LOGGING
241                                 LDAP_LOG ( OPERATION, ERR, 
242                                         "bdb_modify_internal: attribute index add failure\n", 
243                                         0, 0, 0 );
244 #else
245                                 Debug( LDAP_DEBUG_ANY,
246                                        "Attribute index add failure",
247                                        0, 0, 0 );
248 #endif
249                                 return rc;
250                         }
251                         ap->a_flags &= ~SLAP_ATTR_IXADD;
252                 }
253         }
254
255         /* If we've done repeated mods on a cached entry, then e_attrs
256          * is no longer contiguous with the entry.
257          */
258         if( (void *) save_attrs != (void *) (e+1)) {
259                 attrs_free( save_attrs );
260         }
261
262         return rc;
263 }
264
265
266 int
267 bdb_modify( Operation *op, SlapReply *rs )
268 {
269         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
270         Entry           *matched = NULL;
271         Entry           *e = NULL;
272         int             manageDSAit = get_manageDSAit( op );
273         char textbuf[SLAP_TEXT_BUFLEN];
274         size_t textlen = sizeof textbuf;
275         DB_TXN  *ltid = NULL;
276         struct bdb_op_info opinfo;
277
278         u_int32_t       locker = 0;
279         DB_LOCK         lock;
280
281         int             noop = 0;
282
283 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
284         Operation* ps_list;
285         struct psid_entry *pm_list, *pm_prev;
286 #endif
287
288 #ifdef NEW_LOGGING
289         LDAP_LOG ( OPERATION, ENTRY, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
290 #else
291         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
292 #endif
293
294         if( 0 ) {
295 retry:  /* transaction retry */
296                 if( e != NULL ) {
297                         bdb_cache_delete_entry(&bdb->bi_cache, e);
298                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
299                 }
300 #ifdef NEW_LOGGING
301                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: retrying...\n", 0, 0, 0 );
302 #else
303                 Debug(LDAP_DEBUG_TRACE,
304                         "bdb_modify: retrying...\n", 0, 0, 0);
305 #endif
306
307 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
308                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
309                 while ( pm_list != NULL ) {
310                         LDAP_LIST_REMOVE ( pm_list, ps_link );
311                         pm_prev = pm_list;
312                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
313                         ch_free( pm_prev );
314                 }
315 #endif
316
317                 rs->sr_err = TXN_ABORT( ltid );
318                 ltid = NULL;
319                 op->o_private = NULL;
320                 op->o_do_not_cache = opinfo.boi_acl_cache;
321                 if( rs->sr_err != 0 ) {
322                         rs->sr_err = LDAP_OTHER;
323                         rs->sr_text = "internal error";
324                         goto return_results;
325                 }
326                 ldap_pvt_thread_yield();
327         }
328
329         /* begin transaction */
330         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
331                 bdb->bi_db_opflags );
332         rs->sr_text = NULL;
333         if( rs->sr_err != 0 ) {
334 #ifdef NEW_LOGGING
335                 LDAP_LOG ( OPERATION, DETAIL1, 
336                         "bdb_modify: txn_begin failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
337 #else
338                 Debug( LDAP_DEBUG_TRACE,
339                         "bdb_modify: txn_begin failed: %s (%d)\n",
340                         db_strerror(rs->sr_err), rs->sr_err, 0 );
341 #endif
342                 rs->sr_err = LDAP_OTHER;
343                 rs->sr_text = "internal error";
344                 goto return_results;
345         }
346
347         locker = TXN_ID ( ltid );
348
349         opinfo.boi_bdb = op->o_bd;
350         opinfo.boi_txn = ltid;
351         opinfo.boi_locker = locker;
352         opinfo.boi_err = 0;
353         opinfo.boi_acl_cache = op->o_do_not_cache;
354         op->o_private = &opinfo;
355
356         /* get entry */
357         rs->sr_err = bdb_dn2entry_w( op->o_bd, ltid, &op->o_req_ndn, &e, &matched, 0, locker, &lock );
358
359         if ( rs->sr_err != 0 ) {
360 #ifdef NEW_LOGGING
361                 LDAP_LOG ( OPERATION, DETAIL1, 
362                         "bdb_modify: dn2entry failed: (%d)\n", rs->sr_err, 0, 0 );
363 #else
364                 Debug( LDAP_DEBUG_TRACE,
365                         "bdb_modify: dn2entry failed (%d)\n",
366                         rs->sr_err, 0, 0 );
367 #endif
368                 switch( rs->sr_err ) {
369                 case DB_LOCK_DEADLOCK:
370                 case DB_LOCK_NOTGRANTED:
371                         goto retry;
372                 case DB_NOTFOUND:
373                         break;
374                 case LDAP_BUSY:
375                         rs->sr_text = "ldap server busy";
376                         goto return_results;
377                 default:
378                         rs->sr_err = LDAP_OTHER;
379                         rs->sr_text = "internal error";
380                         goto return_results;
381                 }
382         }
383
384         /* acquire and lock entry */
385         if ( e == NULL ) {
386                 if ( matched != NULL ) {
387                         rs->sr_matched = ch_strdup( matched->e_dn );
388                         rs->sr_ref = is_entry_referral( matched )
389                                 ? get_entry_referrals( op, matched )
390                                 : NULL;
391                         bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, matched);
392                         matched = NULL;
393
394                 } else {
395                         rs->sr_ref = referral_rewrite( default_referral,
396                                 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
397                 }
398
399                 rs->sr_err = LDAP_REFERRAL;
400                 send_ldap_result( op, rs );
401
402                 ber_bvarray_free( rs->sr_ref );
403                 free( (char *)rs->sr_matched );
404                 rs->sr_ref = NULL;
405                 rs->sr_matched = NULL;
406
407                 goto done;
408         }
409
410         if ( !manageDSAit && is_entry_referral( e ) ) {
411                 /* entry is a referral, don't allow modify */
412                 rs->sr_ref = get_entry_referrals( op, e );
413
414 #ifdef NEW_LOGGING
415                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: entry is referral\n", 0, 0, 0 );
416 #else
417                 Debug( LDAP_DEBUG_TRACE,
418                         "bdb_modify: entry is referral\n",
419                         0, 0, 0 );
420 #endif
421
422                 rs->sr_err = LDAP_REFERRAL;
423                 rs->sr_matched = e->e_name.bv_val;
424                 send_ldap_result( op, rs );
425
426                 ber_bvarray_free( rs->sr_ref );
427                 rs->sr_ref = NULL;
428                 rs->sr_matched = NULL;
429                 goto done;
430         }
431
432 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
433         if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
434                 LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
435                         bdb_psearch(op, rs, ps_list, e, LDAP_PSEARCH_BY_PREMODIFY );
436                 }
437         }
438 #endif
439         
440         /* Modify the entry */
441         rs->sr_err = bdb_modify_internal( op, ltid, op->oq_modify.rs_modlist, e,
442                 &rs->sr_text, textbuf, textlen );
443
444         if( rs->sr_err != LDAP_SUCCESS ) {
445 #ifdef NEW_LOGGING
446                 LDAP_LOG ( OPERATION, ERR, 
447                         "bdb_modify: modify failed (%d)\n", rs->sr_err, 0, 0 );
448 #else
449                 Debug( LDAP_DEBUG_TRACE,
450                         "bdb_modify: modify failed (%d)\n",
451                         rs->sr_err, 0, 0 );
452 #endif
453                 if ( (rs->sr_err == LDAP_INSUFFICIENT_ACCESS) && opinfo.boi_err ) {
454                         rs->sr_err = opinfo.boi_err;
455                 }
456                 switch( rs->sr_err ) {
457                 case DB_LOCK_DEADLOCK:
458                 case DB_LOCK_NOTGRANTED:
459                         goto retry;
460                 }
461                 goto return_results;
462         }
463
464         /* change the entry itself */
465         rs->sr_err = bdb_id2entry_update( op->o_bd, ltid, e );
466         if ( rs->sr_err != 0 ) {
467 #ifdef NEW_LOGGING
468                 LDAP_LOG ( OPERATION, ERR, 
469                         "bdb_modify: id2entry update failed (%d)\n", rs->sr_err, 0, 0 );
470 #else
471                 Debug( LDAP_DEBUG_TRACE,
472                         "bdb_modify: id2entry update failed (%d)\n",
473                         rs->sr_err, 0, 0 );
474 #endif
475                 switch( rs->sr_err ) {
476                 case DB_LOCK_DEADLOCK:
477                 case DB_LOCK_NOTGRANTED:
478                         goto retry;
479                 }
480                 rs->sr_text = "entry update failed";
481                 goto return_results;
482         }
483
484         if( op->o_noop ) {
485                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
486                         rs->sr_text = "txn_abort (no-op) failed";
487                 } else {
488                         noop = 1;
489                         rs->sr_err = LDAP_SUCCESS;
490                 }
491         } else {
492                 rs->sr_err = TXN_COMMIT( ltid, 0 );
493         }
494         ltid = NULL;
495         op->o_private = NULL;
496
497         if( rs->sr_err != 0 ) {
498 #ifdef NEW_LOGGING
499                 LDAP_LOG ( OPERATION, ERR, 
500                         "bdb_modify: txn_%s failed %s (%d)\n", 
501                         op->o_noop ? "abort (no_op)" : "commit", db_strerror(rs->sr_err), rs->sr_err );
502 #else
503                 Debug( LDAP_DEBUG_TRACE,
504                         "bdb_modify: txn_%s failed: %s (%d)\n",
505                         op->o_noop ? "abort (no-op)" : "commit",
506                         db_strerror(rs->sr_err), rs->sr_err );
507 #endif
508                 rs->sr_err = LDAP_OTHER;
509                 rs->sr_text = "commit failed";
510
511         } else {
512 #ifdef NEW_LOGGING
513                 LDAP_LOG ( OPERATION, DETAIL1, 
514                         "bdb_modify: updated%s id=%08lx dn=\"%s\"\n", 
515                         op->o_noop ? " (no_op)" : "", e->e_id, e->e_dn );
516 #else
517                 Debug( LDAP_DEBUG_TRACE,
518                         "bdb_modify: updated%s id=%08lx dn=\"%s\"\n",
519                         op->o_noop ? " (no-op)" : "",
520                         e->e_id, e->e_dn );
521 #endif
522                 rs->sr_err = LDAP_SUCCESS;
523                 rs->sr_text = NULL;
524         }
525
526 return_results:
527         send_ldap_result( op, rs );
528
529 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
530         if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
531                 /* Loop through in-scope entries for each psearch spec */
532                 LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
533                         bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_MODIFY );
534                 }
535                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
536                 while ( pm_list != NULL ) {
537                         bdb_psearch(op, rs, pm_list->ps_op,
538                                                 e, LDAP_PSEARCH_BY_SCOPEOUT);
539                         LDAP_LIST_REMOVE ( pm_list, ps_link );
540                         pm_prev = pm_list;
541                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
542                         ch_free( pm_prev );
543                 }
544         }
545 #endif
546
547         if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
548                 ldap_pvt_thread_yield();
549                 TXN_CHECKPOINT( bdb->bi_dbenv,
550                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
551         }
552
553 done:
554         if( ltid != NULL ) {
555 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
556                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
557                 while ( pm_list != NULL ) {
558                         LDAP_LIST_REMOVE ( pm_list, ps_link );
559                         pm_prev = pm_list;
560                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
561                         ch_free( pm_prev );
562                 }
563 #endif
564                 TXN_ABORT( ltid );
565                 op->o_private = NULL;
566         }
567
568         if( e != NULL ) {
569                 bdb_unlocked_cache_return_entry_w (&bdb->bi_cache, e);
570         }
571         return ( ( rs->sr_err == LDAP_SUCCESS ) ? noop : rs->sr_err );
572 }