]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
Sync with HEAD
[openldap] / servers / slapd / back-bdb / modify.c
1 /* modify.c - bdb backend modify routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2004 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21 #include <ac/time.h>
22
23 #include "back-bdb.h"
24 #include "external.h"
25
26 int bdb_modify_internal(
27         Operation *op,
28         DB_TXN *tid,
29         Modifications *modlist,
30         Entry *e,
31         const char **text,
32         char *textbuf,
33         size_t textlen )
34 {
35         int rc, err;
36         Modification    *mod;
37         Modifications   *ml;
38         Attribute       *save_attrs;
39         Attribute       *ap;
40         int                     glue_attr_delete = 0;
41
42 #ifdef NEW_LOGGING
43         LDAP_LOG ( OPERATION, ENTRY, "bdb_modify_internal: 0x%08lx: %s\n", 
44                 e->e_id, e->e_dn, 0 );
45 #else
46         Debug( LDAP_DEBUG_TRACE, "bdb_modify_internal: 0x%08lx: %s\n",
47                 e->e_id, e->e_dn, 0);
48 #endif
49
50         if ( !acl_check_modlist( op, e, modlist )) {
51                 return LDAP_INSUFFICIENT_ACCESS;
52         }
53
54         /* save_attrs will be disposed of by bdb_cache_modify */
55         save_attrs = e->e_attrs;
56         e->e_attrs = attrs_dup( e->e_attrs );
57
58         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
59                 mod = &ml->sml_mod;
60                 switch( mod->sm_op ) {
61                 case LDAP_MOD_ADD:
62                 case LDAP_MOD_REPLACE:
63                         if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
64                         /* sc modify is internally allowed only to make an entry a glue */
65                                 glue_attr_delete = 1;
66                         }
67                 }
68                 if ( glue_attr_delete )
69                         break;
70         }
71
72         if ( glue_attr_delete ) {
73                 Attribute       **app = &e->e_attrs;
74                 while ( *app != NULL ) {
75                         if ( !is_at_operational( (*app)->a_desc->ad_type )) {
76                                 Attribute *save = *app;
77                                 *app = (*app)->a_next;
78                                 attr_free( save );
79                                 continue;
80                         }
81                         app = &(*app)->a_next;
82                 }
83         }
84
85         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
86                 mod = &ml->sml_mod;
87
88                 switch ( mod->sm_op ) {
89                 case LDAP_MOD_ADD:
90 #ifdef NEW_LOGGING
91                         LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify_internal: add\n", 0,0,0);
92 #else
93                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: add\n", 0, 0, 0);
94 #endif
95                         err = modify_add_values( e, mod, get_permissiveModify(op),
96                                 text, textbuf, textlen );
97                         if( err != LDAP_SUCCESS ) {
98 #ifdef NEW_LOGGING
99                                 LDAP_LOG ( OPERATION, ERR, 
100                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
101 #else
102                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
103                                         err, *text, 0);
104 #endif
105                         }
106                         break;
107
108                 case LDAP_MOD_DELETE:
109                         if ( glue_attr_delete )
110                                 break;
111 #ifdef NEW_LOGGING
112                         LDAP_LOG ( OPERATION, DETAIL1, 
113                                 "bdb_modify_internal: delete\n", 0, 0, 0 );
114 #else
115                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: delete\n", 0, 0, 0);
116 #endif
117                         err = modify_delete_values( e, mod, get_permissiveModify(op),
118                                 text, textbuf, textlen );
119                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
120                         if( err != LDAP_SUCCESS ) {
121 #ifdef NEW_LOGGING
122                                 LDAP_LOG ( OPERATION, ERR, 
123                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
124 #else
125                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
126                                         err, *text, 0);
127 #endif
128                         }
129                         break;
130
131                 case LDAP_MOD_REPLACE:
132 #ifdef NEW_LOGGING
133                         LDAP_LOG ( OPERATION, DETAIL1, 
134                                 "bdb_modify_internal: replace\n", 0, 0, 0 );
135 #else
136                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: replace\n", 0, 0, 0);
137 #endif
138                         err = modify_replace_values( e, mod, get_permissiveModify(op),
139                                 text, textbuf, textlen );
140                         if( err != LDAP_SUCCESS ) {
141 #ifdef NEW_LOGGING
142                                 LDAP_LOG ( OPERATION, ERR, 
143                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
144 #else
145                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
146                                         err, *text, 0);
147 #endif
148                         }
149                         break;
150
151                 case LDAP_MOD_INCREMENT:
152 #ifdef NEW_LOGGING
153                         LDAP_LOG ( OPERATION, DETAIL1, 
154                                 "bdb_modify_internal: increment\n", 0, 0, 0 );
155 #else
156                         Debug(LDAP_DEBUG_ARGS,
157                                 "bdb_modify_internal: increment\n", 0, 0, 0);
158 #endif
159                         err = modify_increment_values( e, mod, get_permissiveModify(op),
160                                 text, textbuf, textlen );
161                         if( err != LDAP_SUCCESS ) {
162 #ifdef NEW_LOGGING
163                                 LDAP_LOG ( OPERATION, ERR, 
164                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
165 #else
166                                 Debug(LDAP_DEBUG_ARGS,
167                                         "bdb_modify_internal: %d %s\n",
168                                         err, *text, 0);
169 #endif
170                         }
171                         break;
172
173                 case SLAP_MOD_SOFTADD:
174 #ifdef NEW_LOGGING
175                         LDAP_LOG ( OPERATION, DETAIL1, 
176                                 "bdb_modify_internal: softadd\n",0,0,0 );
177 #else
178                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: softadd\n", 0, 0, 0);
179 #endif
180                         /* Avoid problems in index_add_mods()
181                          * We need to add index if necessary.
182                          */
183                         mod->sm_op = LDAP_MOD_ADD;
184
185                         err = modify_add_values( e, mod, get_permissiveModify(op),
186                                 text, textbuf, textlen );
187
188                         mod->sm_op = SLAP_MOD_SOFTADD;
189
190                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
191                                 err = LDAP_SUCCESS;
192                         }
193
194                         if( err != LDAP_SUCCESS ) {
195 #ifdef NEW_LOGGING
196                                 LDAP_LOG ( OPERATION, ERR, 
197                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
198 #else
199                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
200                                         err, *text, 0);
201 #endif
202                         }
203                         break;
204
205                 default:
206 #ifdef NEW_LOGGING
207                                 LDAP_LOG ( OPERATION, ERR, 
208                                         "bdb_modify_internal: invalid op %d\n", mod->sm_op, 0, 0 );
209 #else
210                         Debug(LDAP_DEBUG_ANY, "bdb_modify_internal: invalid op %d\n",
211                                 mod->sm_op, 0, 0);
212 #endif
213                         *text = "Invalid modify operation";
214                         err = LDAP_OTHER;
215 #ifdef NEW_LOGGING
216                                 LDAP_LOG ( OPERATION, ERR, 
217                                         "bdb_modify_internal: %d %s\n", err, *text, 0 );
218 #else
219                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
220                                 err, *text, 0);
221 #endif
222                 }
223
224                 if ( err != LDAP_SUCCESS ) {
225                         attrs_free( e->e_attrs );
226                         e->e_attrs = save_attrs;
227                         /* unlock entry, delete from cache */
228                         return err; 
229                 }
230
231                 /* If objectClass was modified, reset the flags */
232                 if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
233                         e->e_ocflags = 0;
234                 }
235
236                 if ( glue_attr_delete ) {
237                         e->e_ocflags = 0;
238                 }
239
240                 /* check if modified attribute was indexed
241                  * but not in case of NOOP... */
242                 err = bdb_index_is_indexed( op->o_bd, mod->sm_desc );
243                 if ( err == LDAP_SUCCESS && !op->o_noop ) {
244                         ap = attr_find( save_attrs, mod->sm_desc );
245                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
246
247                         ap = attr_find( e->e_attrs, mod->sm_desc );
248                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
249                 }
250         }
251
252         /* check that the entry still obeys the schema */
253         rc = entry_schema_check( op->o_bd, e, save_attrs, text, textbuf, textlen );
254         if ( rc != LDAP_SUCCESS || op->o_noop ) {
255                 attrs_free( e->e_attrs );
256                 e->e_attrs = save_attrs;
257
258                 if ( rc != LDAP_SUCCESS ) {
259 #ifdef NEW_LOGGING
260                         LDAP_LOG ( OPERATION, ERR, "bdb_modify_internal: "
261                                 "entry failed schema check %s\n", 
262                                 *text, 0, 0 );
263 #else
264                         Debug( LDAP_DEBUG_ANY,
265                                 "entry failed schema check: %s\n",
266                                 *text, 0, 0 );
267 #endif
268                 }
269
270                 /* if NOOP then silently revert to saved attrs */
271                 return rc;
272         }
273
274         /* update the indices of the modified attributes */
275
276         /* start with deleting the old index entries */
277         for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
278                 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
279                         rc = bdb_index_values( op, tid, ap->a_desc,
280                                 ap->a_nvals,
281                                 e->e_id, SLAP_INDEX_DELETE_OP );
282                         if ( rc != LDAP_SUCCESS ) {
283                                 attrs_free( e->e_attrs );
284                                 e->e_attrs = save_attrs;
285 #ifdef NEW_LOGGING
286                                 LDAP_LOG ( OPERATION, ERR, 
287                                         "bdb_modify_internal: attribute index delete failure\n",
288                                         0, 0, 0 );
289 #else
290                                 Debug( LDAP_DEBUG_ANY,
291                                        "Attribute index delete failure",
292                                        0, 0, 0 );
293 #endif
294                                 return rc;
295                         }
296                         ap->a_flags &= ~SLAP_ATTR_IXDEL;
297                 }
298         }
299
300         /* add the new index entries */
301         for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
302                 if (ap->a_flags & SLAP_ATTR_IXADD) {
303                         rc = bdb_index_values( op, tid, ap->a_desc,
304                                 ap->a_nvals,
305                                 e->e_id, SLAP_INDEX_ADD_OP );
306                         if ( rc != LDAP_SUCCESS ) {
307                                 attrs_free( e->e_attrs );
308                                 e->e_attrs = save_attrs;
309 #ifdef NEW_LOGGING
310                                 LDAP_LOG ( OPERATION, ERR, 
311                                         "bdb_modify_internal: attribute index add failure\n", 
312                                         0, 0, 0 );
313 #else
314                                 Debug( LDAP_DEBUG_ANY,
315                                        "Attribute index add failure",
316                                        0, 0, 0 );
317 #endif
318                                 return rc;
319                         }
320                         ap->a_flags &= ~SLAP_ATTR_IXADD;
321                 }
322         }
323
324         return rc;
325 }
326
327
328 int
329 bdb_modify( Operation *op, SlapReply *rs )
330 {
331         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
332         Entry           *e = NULL;
333         EntryInfo       *ei = NULL;
334         int             manageDSAit = get_manageDSAit( op );
335         char textbuf[SLAP_TEXT_BUFLEN];
336         size_t textlen = sizeof textbuf;
337         DB_TXN  *ltid = NULL, *lt2;
338         struct bdb_op_info opinfo;
339         Entry           dummy;
340
341         u_int32_t       locker = 0;
342         DB_LOCK         lock;
343
344         int             num_retries = 0;
345
346         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
347         int num_ctrls = 0;
348
349         Operation* ps_list;
350         struct psid_entry *pm_list, *pm_prev;
351         int rc;
352         EntryInfo       *suffix_ei;
353         Entry           *ctxcsn_e;
354         int                     ctxcsn_added = 0;
355
356 #ifdef NEW_LOGGING
357         LDAP_LOG ( OPERATION, ENTRY, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
358 #else
359         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
360 #endif
361
362         if( 0 ) {
363 retry:  /* transaction retry */
364                 if( e != NULL ) {
365                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
366                         e = NULL;
367                 }
368 #ifdef NEW_LOGGING
369                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: retrying...\n", 0, 0, 0 );
370 #else
371                 Debug(LDAP_DEBUG_TRACE,
372                         "bdb_modify: retrying...\n", 0, 0, 0);
373 #endif
374
375                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
376                 while ( pm_list != NULL ) {
377                         LDAP_LIST_REMOVE ( pm_list, ps_link );
378                         pm_prev = pm_list;
379                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
380                         ch_free( pm_prev );
381                 }
382
383                 rs->sr_err = TXN_ABORT( ltid );
384                 ltid = NULL;
385                 op->o_private = NULL;
386                 op->o_do_not_cache = opinfo.boi_acl_cache;
387                 if( rs->sr_err != 0 ) {
388                         rs->sr_err = LDAP_OTHER;
389                         rs->sr_text = "internal error";
390                         goto return_results;
391                 }
392                 ldap_pvt_thread_yield();
393                 bdb_trans_backoff( ++num_retries );
394         }
395
396         /* begin transaction */
397         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
398                 bdb->bi_db_opflags );
399         rs->sr_text = NULL;
400         if( rs->sr_err != 0 ) {
401 #ifdef NEW_LOGGING
402                 LDAP_LOG ( OPERATION, DETAIL1, 
403                         "bdb_modify: txn_begin failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
404 #else
405                 Debug( LDAP_DEBUG_TRACE,
406                         "bdb_modify: txn_begin failed: %s (%d)\n",
407                         db_strerror(rs->sr_err), rs->sr_err, 0 );
408 #endif
409                 rs->sr_err = LDAP_OTHER;
410                 rs->sr_text = "internal error";
411                 goto return_results;
412         }
413
414         locker = TXN_ID ( ltid );
415
416         opinfo.boi_bdb = op->o_bd;
417         opinfo.boi_txn = ltid;
418         opinfo.boi_locker = locker;
419         opinfo.boi_err = 0;
420         opinfo.boi_acl_cache = op->o_do_not_cache;
421         op->o_private = &opinfo;
422
423         /* get entry or ancestor */
424         rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
425                 locker, &lock );
426
427         if ( rs->sr_err != 0 ) {
428 #ifdef NEW_LOGGING
429                 LDAP_LOG ( OPERATION, DETAIL1, 
430                         "bdb_modify: dn2entry failed: (%d)\n", rs->sr_err, 0, 0 );
431 #else
432                 Debug( LDAP_DEBUG_TRACE,
433                         "bdb_modify: dn2entry failed (%d)\n",
434                         rs->sr_err, 0, 0 );
435 #endif
436                 switch( rs->sr_err ) {
437                 case DB_LOCK_DEADLOCK:
438                 case DB_LOCK_NOTGRANTED:
439                         goto retry;
440                 case DB_NOTFOUND:
441                         break;
442                 case LDAP_BUSY:
443                         rs->sr_text = "ldap server busy";
444                         goto return_results;
445                 default:
446                         rs->sr_err = LDAP_OTHER;
447                         rs->sr_text = "internal error";
448                         goto return_results;
449                 }
450         }
451
452         e = ei->bei_e;
453         /* acquire and lock entry */
454         /* FIXME: dn2entry() should return non-glue entry */
455         if (( rs->sr_err == DB_NOTFOUND ) || ( !manageDSAit && e && is_entry_glue( e ))) {
456                 if ( e != NULL ) {
457                         rs->sr_matched = ch_strdup( e->e_dn );
458                         rs->sr_ref = is_entry_referral( e )
459                                 ? get_entry_referrals( op, e )
460                                 : NULL;
461                         bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, e);
462                         e = NULL;
463
464                 } else {
465                         BerVarray deref = NULL;
466                         if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
467                                 syncinfo_t *si;
468                                 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
469                                         struct berval tmpbv;
470                                         ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
471                                         ber_bvarray_add( &deref, &tmpbv );
472                 }
473                         } else {
474                                 deref = default_referral;
475                         }
476                         rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
477                                 LDAP_SCOPE_DEFAULT );
478                 }
479
480                 rs->sr_err = LDAP_REFERRAL;
481                 send_ldap_result( op, rs );
482
483                 if ( rs->sr_ref != default_referral ) {
484                         ber_bvarray_free( rs->sr_ref );
485                 }
486                 free( (char *)rs->sr_matched );
487                 rs->sr_ref = NULL;
488                 rs->sr_matched = NULL;
489
490                 goto done;
491         }
492
493         if ( !manageDSAit && is_entry_referral( e ) ) {
494                 /* entry is a referral, don't allow modify */
495                 rs->sr_ref = get_entry_referrals( op, e );
496
497 #ifdef NEW_LOGGING
498                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: entry is referral\n", 0, 0, 0 );
499 #else
500                 Debug( LDAP_DEBUG_TRACE,
501                         "bdb_modify: entry is referral\n",
502                         0, 0, 0 );
503 #endif
504
505                 rs->sr_err = LDAP_REFERRAL;
506                 rs->sr_matched = e->e_name.bv_val;
507                 send_ldap_result( op, rs );
508
509                 ber_bvarray_free( rs->sr_ref );
510                 rs->sr_ref = NULL;
511                 rs->sr_matched = NULL;
512                 goto done;
513         }
514
515         if ( get_assert( op ) &&
516                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
517         {
518                 rs->sr_err = LDAP_ASSERTION_FAILED;
519                 goto return_results;
520         }
521
522         if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop && !op->o_no_psearch ) {
523                 ldap_pvt_thread_rdwr_rlock( &bdb->bi_pslist_rwlock );
524                 LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
525                         bdb_psearch(op, rs, ps_list, e, LDAP_PSEARCH_BY_PREMODIFY );
526                 }
527                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
528         }
529
530         if( op->o_preread ) {
531                 if ( slap_read_controls( op, rs, e,
532                         &slap_pre_read_bv, &ctrls[num_ctrls] ) )
533                 {
534 #ifdef NEW_LOGGING
535                         LDAP_LOG ( OPERATION, DETAIL1,
536                                 "<=- bdb_modify: pre-read failed!\n", 0, 0, 0 );
537 #else
538                         Debug( LDAP_DEBUG_TRACE,
539                                 "<=- bdb_modify: pre-read failed!\n", 0, 0, 0 );
540 #endif
541                         goto return_results;
542                 }
543                 ctrls[++num_ctrls] = NULL;
544                 op->o_preread = 0; /* prevent redo on retry */
545         }
546
547         /* nested transaction */
548         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
549                 bdb->bi_db_opflags );
550         rs->sr_text = NULL;
551         if( rs->sr_err != 0 ) {
552 #ifdef NEW_LOGGING
553                 LDAP_LOG ( OPERATION, ERR, 
554                         "bdb_modify: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
555 #else
556                 Debug( LDAP_DEBUG_TRACE,
557                         "bdb_modify: txn_begin(2) failed: %s (%d)\n",
558                         db_strerror(rs->sr_err), rs->sr_err, 0 );
559 #endif
560                 rs->sr_err = LDAP_OTHER;
561                 rs->sr_text = "internal error";
562                 goto return_results;
563         }
564         /* Modify the entry */
565         dummy = *e;
566         rs->sr_err = bdb_modify_internal( op, lt2, op->oq_modify.rs_modlist,
567                 &dummy, &rs->sr_text, textbuf, textlen );
568
569         if( rs->sr_err != LDAP_SUCCESS ) {
570 #ifdef NEW_LOGGING
571                 LDAP_LOG ( OPERATION, ERR, 
572                         "bdb_modify: modify failed (%d)\n", rs->sr_err, 0, 0 );
573 #else
574                 Debug( LDAP_DEBUG_TRACE,
575                         "bdb_modify: modify failed (%d)\n",
576                         rs->sr_err, 0, 0 );
577 #endif
578                 if ( (rs->sr_err == LDAP_INSUFFICIENT_ACCESS) && opinfo.boi_err ) {
579                         rs->sr_err = opinfo.boi_err;
580                 }
581                 switch( rs->sr_err ) {
582                 case DB_LOCK_DEADLOCK:
583                 case DB_LOCK_NOTGRANTED:
584                         goto retry;
585                 }
586                 goto return_results;
587         }
588
589         if( op->o_postread ) {
590                 if( slap_read_controls( op, rs, e,
591                         &slap_post_read_bv, &ctrls[num_ctrls] ) )
592                 {
593 #ifdef NEW_LOGGING
594                         LDAP_LOG ( OPERATION, DETAIL1,
595                                 "<=- bdb_modify: post-read failed!\n", 0, 0, 0 );
596 #else
597                         Debug( LDAP_DEBUG_TRACE,
598                                 "<=- bdb_modify: post-read failed!\n", 0, 0, 0 );
599 #endif
600                         goto return_results;
601                 }
602                 ctrls[++num_ctrls] = NULL;
603                 op->o_postread = 0;  /* prevent redo on retry */
604                 /* FIXME: should read entry on the last retry */
605         }
606
607         /* change the entry itself */
608         rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, &dummy );
609         if ( rs->sr_err != 0 ) {
610 #ifdef NEW_LOGGING
611                 LDAP_LOG ( OPERATION, ERR, 
612                         "bdb_modify: id2entry update failed (%d)\n", rs->sr_err, 0, 0 );
613 #else
614                 Debug( LDAP_DEBUG_TRACE,
615                         "bdb_modify: id2entry update failed (%d)\n",
616                         rs->sr_err, 0, 0 );
617 #endif
618                 switch( rs->sr_err ) {
619                 case DB_LOCK_DEADLOCK:
620                 case DB_LOCK_NOTGRANTED:
621                         goto retry;
622                 }
623                 rs->sr_text = "entry update failed";
624                 goto return_results;
625         }
626
627         if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
628                 rs->sr_err = LDAP_OTHER;
629                 rs->sr_text = "txn_commit(2) failed";
630                 goto return_results;
631         }
632
633         if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
634                 rc = bdb_csn_commit( op, rs, ltid, ei, &suffix_ei,
635                         &ctxcsn_e, &ctxcsn_added, locker );
636                 switch ( rc ) {
637                 case BDB_CSN_ABORT :
638                         goto return_results;
639                 case BDB_CSN_RETRY :
640                         goto retry;
641                 }
642         }
643
644         if( op->o_noop ) {
645                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
646                         rs->sr_text = "txn_abort (no-op) failed";
647                 } else {
648                         rs->sr_err = LDAP_NO_OPERATION;
649                         goto return_results;
650                 }
651         } else {
652                 bdb_cache_modify( e, dummy.e_attrs, bdb->bi_dbenv, locker, &lock );
653
654                 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
655                         if ( ctxcsn_added ) {
656                                 bdb_cache_add( bdb, suffix_ei, ctxcsn_e,
657                                         (struct berval *)&slap_ldapsync_cn_bv, locker );
658                         }
659                 }
660
661                 if ( rs->sr_err == LDAP_SUCCESS ) {
662                         /* Loop through in-scope entries for each psearch spec */
663                         ldap_pvt_thread_rdwr_rlock( &bdb->bi_pslist_rwlock );
664                         LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
665                                 bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_MODIFY );
666                         }
667                         ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
668                         pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
669                         while ( pm_list != NULL ) {
670                                 bdb_psearch(op, rs, pm_list->ps_op,
671                                                         e, LDAP_PSEARCH_BY_SCOPEOUT);
672                                 LDAP_LIST_REMOVE ( pm_list, ps_link );
673                                 pm_prev = pm_list;
674                                 pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
675                                 ch_free( pm_prev );
676                         }
677                 }
678
679                 rs->sr_err = TXN_COMMIT( ltid, 0 );
680         }
681         ltid = NULL;
682         op->o_private = NULL;
683
684         if( rs->sr_err != 0 ) {
685 #ifdef NEW_LOGGING
686                 LDAP_LOG ( OPERATION, ERR, 
687                         "bdb_modify: txn_%s failed %s (%d)\n", 
688                         op->o_noop ? "abort (no_op)" : "commit",
689                         db_strerror(rs->sr_err), rs->sr_err );
690 #else
691                 Debug( LDAP_DEBUG_TRACE,
692                         "bdb_modify: txn_%s failed: %s (%d)\n",
693                         op->o_noop ? "abort (no-op)" : "commit",
694                         db_strerror(rs->sr_err), rs->sr_err );
695 #endif
696                 rs->sr_err = LDAP_OTHER;
697                 rs->sr_text = "commit failed";
698
699                 goto return_results;
700         }
701
702 #ifdef NEW_LOGGING
703         LDAP_LOG ( OPERATION, DETAIL1, 
704                 "bdb_modify: updated%s id=%08lx dn=\"%s\"\n", 
705                 op->o_noop ? " (no_op)" : "", e->e_id, e->e_dn );
706 #else
707         Debug( LDAP_DEBUG_TRACE,
708                 "bdb_modify: updated%s id=%08lx dn=\"%s\"\n",
709                 op->o_noop ? " (no-op)" : "",
710                 e->e_id, e->e_dn );
711 #endif
712
713         rs->sr_err = LDAP_SUCCESS;
714         rs->sr_text = NULL;
715         if( num_ctrls ) rs->sr_ctrls = ctrls;
716
717 return_results:
718         send_ldap_result( op, rs );
719
720         if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
721                 ldap_pvt_thread_yield();
722                 TXN_CHECKPOINT( bdb->bi_dbenv,
723                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
724         }
725
726 done:
727         if( ltid != NULL ) {
728                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
729                 while ( pm_list != NULL ) {
730                         LDAP_LIST_REMOVE ( pm_list, ps_link );
731                         pm_prev = pm_list;
732                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
733                         ch_free( pm_prev );
734                 }
735                 TXN_ABORT( ltid );
736                 op->o_private = NULL;
737         }
738
739         if( e != NULL ) {
740                 bdb_unlocked_cache_return_entry_w (&bdb->bi_cache, e);
741         }
742         return rs->sr_err;
743 }