]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
2f1df81bdaf7e4a12ac2b2c6af0b97fee2c4697e
[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 #ifdef SLAP_NVALUES
212                                 ap->a_nvals,
213 #else
214                                 ap->a_vals,
215 #endif
216                                 e->e_id, SLAP_INDEX_DELETE_OP );
217                         if ( rc != LDAP_SUCCESS ) {
218                                 attrs_free( e->e_attrs );
219                                 e->e_attrs = save_attrs;
220 #ifdef NEW_LOGGING
221                                 LDAP_LOG ( OPERATION, ERR, 
222                                         "bdb_modify_internal: attribute index delete failure\n",
223                                         0, 0, 0 );
224 #else
225                                 Debug( LDAP_DEBUG_ANY,
226                                        "Attribute index delete failure",
227                                        0, 0, 0 );
228 #endif
229                                 return rc;
230                         }
231                         ap->a_flags &= ~SLAP_ATTR_IXDEL;
232                 }
233         }
234
235         /* add the new index entries */
236         for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
237                 if (ap->a_flags & SLAP_ATTR_IXADD) {
238                         rc = bdb_index_values( op->o_bd, tid, ap->a_desc,
239 #ifdef SLAP_NVALUES
240                                 ap->a_nvals,
241 #else
242                                 ap->a_vals,
243 #endif
244                                 e->e_id, SLAP_INDEX_ADD_OP );
245                         if ( rc != LDAP_SUCCESS ) {
246                                 attrs_free( e->e_attrs );
247                                 e->e_attrs = save_attrs;
248 #ifdef NEW_LOGGING
249                                 LDAP_LOG ( OPERATION, ERR, 
250                                         "bdb_modify_internal: attribute index add failure\n", 
251                                         0, 0, 0 );
252 #else
253                                 Debug( LDAP_DEBUG_ANY,
254                                        "Attribute index add failure",
255                                        0, 0, 0 );
256 #endif
257                                 return rc;
258                         }
259                         ap->a_flags &= ~SLAP_ATTR_IXADD;
260                 }
261         }
262
263         /* If we've done repeated mods on a cached entry, then e_attrs
264          * is no longer contiguous with the entry.
265          */
266         if( (void *) save_attrs != (void *) (e+1)) {
267                 attrs_free( save_attrs );
268         }
269
270         return rc;
271 }
272
273
274 int
275 bdb_modify( Operation *op, SlapReply *rs )
276 {
277         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
278         Entry           *matched = NULL;
279         Entry           *e = NULL;
280         int             manageDSAit = get_manageDSAit( op );
281         char textbuf[SLAP_TEXT_BUFLEN];
282         size_t textlen = sizeof textbuf;
283         DB_TXN  *ltid = NULL;
284         struct bdb_op_info opinfo;
285
286         u_int32_t       locker = 0;
287         DB_LOCK         lock;
288
289         int             noop = 0;
290
291 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
292         Operation* ps_list;
293         struct psid_entry *pm_list, *pm_prev;
294 #endif
295
296 #ifdef NEW_LOGGING
297         LDAP_LOG ( OPERATION, ENTRY, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
298 #else
299         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", op->o_req_dn.bv_val, 0, 0 );
300 #endif
301
302         if( 0 ) {
303 retry:  /* transaction retry */
304                 if( e != NULL ) {
305                         bdb_cache_delete_entry(&bdb->bi_cache, e);
306                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
307                 }
308 #ifdef NEW_LOGGING
309                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: retrying...\n", 0, 0, 0 );
310 #else
311                 Debug(LDAP_DEBUG_TRACE,
312                         "bdb_modify: retrying...\n", 0, 0, 0);
313 #endif
314
315 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
316                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
317                 while ( pm_list != NULL ) {
318                         LDAP_LIST_REMOVE ( pm_list, ps_link );
319                         pm_prev = pm_list;
320                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
321                         ch_free( pm_prev );
322                 }
323 #endif
324
325                 rs->sr_err = TXN_ABORT( ltid );
326                 ltid = NULL;
327                 op->o_private = NULL;
328                 op->o_do_not_cache = opinfo.boi_acl_cache;
329                 if( rs->sr_err != 0 ) {
330                         rs->sr_err = LDAP_OTHER;
331                         rs->sr_text = "internal error";
332                         goto return_results;
333                 }
334                 ldap_pvt_thread_yield();
335         }
336
337         /* begin transaction */
338         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
339                 bdb->bi_db_opflags );
340         rs->sr_text = NULL;
341         if( rs->sr_err != 0 ) {
342 #ifdef NEW_LOGGING
343                 LDAP_LOG ( OPERATION, DETAIL1, 
344                         "bdb_modify: txn_begin failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
345 #else
346                 Debug( LDAP_DEBUG_TRACE,
347                         "bdb_modify: txn_begin failed: %s (%d)\n",
348                         db_strerror(rs->sr_err), rs->sr_err, 0 );
349 #endif
350                 rs->sr_err = LDAP_OTHER;
351                 rs->sr_text = "internal error";
352                 goto return_results;
353         }
354
355         locker = TXN_ID ( ltid );
356
357         opinfo.boi_bdb = op->o_bd;
358         opinfo.boi_txn = ltid;
359         opinfo.boi_locker = locker;
360         opinfo.boi_err = 0;
361         opinfo.boi_acl_cache = op->o_do_not_cache;
362         op->o_private = &opinfo;
363
364         /* get entry */
365         rs->sr_err = bdb_dn2entry_w( op->o_bd, ltid, &op->o_req_ndn, &e, &matched, 0, locker, &lock );
366
367         if ( rs->sr_err != 0 ) {
368 #ifdef NEW_LOGGING
369                 LDAP_LOG ( OPERATION, DETAIL1, 
370                         "bdb_modify: dn2entry failed: (%d)\n", rs->sr_err, 0, 0 );
371 #else
372                 Debug( LDAP_DEBUG_TRACE,
373                         "bdb_modify: dn2entry failed (%d)\n",
374                         rs->sr_err, 0, 0 );
375 #endif
376                 switch( rs->sr_err ) {
377                 case DB_LOCK_DEADLOCK:
378                 case DB_LOCK_NOTGRANTED:
379                         goto retry;
380                 case DB_NOTFOUND:
381                         break;
382                 case LDAP_BUSY:
383                         rs->sr_text = "ldap server busy";
384                         goto return_results;
385                 default:
386                         rs->sr_err = LDAP_OTHER;
387                         rs->sr_text = "internal error";
388                         goto return_results;
389                 }
390         }
391
392         /* acquire and lock entry */
393         if ( e == NULL ) {
394                 if ( matched != NULL ) {
395                         rs->sr_matched = ch_strdup( matched->e_dn );
396                         rs->sr_ref = is_entry_referral( matched )
397                                 ? get_entry_referrals( op, matched )
398                                 : NULL;
399                         bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, matched);
400                         matched = NULL;
401
402                 } else {
403                         rs->sr_ref = referral_rewrite( default_referral,
404                                 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
405                 }
406
407                 rs->sr_err = LDAP_REFERRAL;
408                 send_ldap_result( op, rs );
409
410                 ber_bvarray_free( rs->sr_ref );
411                 free( (char *)rs->sr_matched );
412                 rs->sr_ref = NULL;
413                 rs->sr_matched = NULL;
414
415                 goto done;
416         }
417
418         if ( !manageDSAit && is_entry_referral( e ) ) {
419                 /* entry is a referral, don't allow modify */
420                 rs->sr_ref = get_entry_referrals( op, e );
421
422 #ifdef NEW_LOGGING
423                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: entry is referral\n", 0, 0, 0 );
424 #else
425                 Debug( LDAP_DEBUG_TRACE,
426                         "bdb_modify: entry is referral\n",
427                         0, 0, 0 );
428 #endif
429
430                 rs->sr_err = LDAP_REFERRAL;
431                 rs->sr_matched = e->e_name.bv_val;
432                 send_ldap_result( op, rs );
433
434                 ber_bvarray_free( rs->sr_ref );
435                 rs->sr_ref = NULL;
436                 rs->sr_matched = NULL;
437                 goto done;
438         }
439
440 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
441         if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
442                 LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
443                         bdb_psearch(op, rs, ps_list, e, LDAP_PSEARCH_BY_PREMODIFY );
444                 }
445         }
446 #endif
447         
448         /* Modify the entry */
449         rs->sr_err = bdb_modify_internal( op, ltid, op->oq_modify.rs_modlist, e,
450                 &rs->sr_text, textbuf, textlen );
451
452         if( rs->sr_err != LDAP_SUCCESS ) {
453 #ifdef NEW_LOGGING
454                 LDAP_LOG ( OPERATION, ERR, 
455                         "bdb_modify: modify failed (%d)\n", rs->sr_err, 0, 0 );
456 #else
457                 Debug( LDAP_DEBUG_TRACE,
458                         "bdb_modify: modify failed (%d)\n",
459                         rs->sr_err, 0, 0 );
460 #endif
461                 if ( (rs->sr_err == LDAP_INSUFFICIENT_ACCESS) && opinfo.boi_err ) {
462                         rs->sr_err = opinfo.boi_err;
463                 }
464                 switch( rs->sr_err ) {
465                 case DB_LOCK_DEADLOCK:
466                 case DB_LOCK_NOTGRANTED:
467                         goto retry;
468                 }
469                 goto return_results;
470         }
471
472         /* change the entry itself */
473         rs->sr_err = bdb_id2entry_update( op->o_bd, ltid, e );
474         if ( rs->sr_err != 0 ) {
475 #ifdef NEW_LOGGING
476                 LDAP_LOG ( OPERATION, ERR, 
477                         "bdb_modify: id2entry update failed (%d)\n", rs->sr_err, 0, 0 );
478 #else
479                 Debug( LDAP_DEBUG_TRACE,
480                         "bdb_modify: id2entry update failed (%d)\n",
481                         rs->sr_err, 0, 0 );
482 #endif
483                 switch( rs->sr_err ) {
484                 case DB_LOCK_DEADLOCK:
485                 case DB_LOCK_NOTGRANTED:
486                         goto retry;
487                 }
488                 rs->sr_text = "entry update failed";
489                 goto return_results;
490         }
491
492         if( op->o_noop ) {
493                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
494                         rs->sr_text = "txn_abort (no-op) failed";
495                 } else {
496                         noop = 1;
497                         rs->sr_err = LDAP_SUCCESS;
498                 }
499         } else {
500                 rs->sr_err = TXN_COMMIT( ltid, 0 );
501         }
502         ltid = NULL;
503         op->o_private = NULL;
504
505         if( rs->sr_err != 0 ) {
506 #ifdef NEW_LOGGING
507                 LDAP_LOG ( OPERATION, ERR, 
508                         "bdb_modify: txn_%s failed %s (%d)\n", 
509                         op->o_noop ? "abort (no_op)" : "commit", db_strerror(rs->sr_err), rs->sr_err );
510 #else
511                 Debug( LDAP_DEBUG_TRACE,
512                         "bdb_modify: txn_%s failed: %s (%d)\n",
513                         op->o_noop ? "abort (no-op)" : "commit",
514                         db_strerror(rs->sr_err), rs->sr_err );
515 #endif
516                 rs->sr_err = LDAP_OTHER;
517                 rs->sr_text = "commit failed";
518
519         } else {
520 #ifdef NEW_LOGGING
521                 LDAP_LOG ( OPERATION, DETAIL1, 
522                         "bdb_modify: updated%s id=%08lx dn=\"%s\"\n", 
523                         op->o_noop ? " (no_op)" : "", e->e_id, e->e_dn );
524 #else
525                 Debug( LDAP_DEBUG_TRACE,
526                         "bdb_modify: updated%s id=%08lx dn=\"%s\"\n",
527                         op->o_noop ? " (no-op)" : "",
528                         e->e_id, e->e_dn );
529 #endif
530                 rs->sr_err = LDAP_SUCCESS;
531                 rs->sr_text = NULL;
532         }
533
534 return_results:
535         send_ldap_result( op, rs );
536
537 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
538         if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
539                 /* Loop through in-scope entries for each psearch spec */
540                 LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
541                         bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_MODIFY );
542                 }
543                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
544                 while ( pm_list != NULL ) {
545                         bdb_psearch(op, rs, pm_list->ps_op,
546                                                 e, LDAP_PSEARCH_BY_SCOPEOUT);
547                         LDAP_LIST_REMOVE ( pm_list, ps_link );
548                         pm_prev = pm_list;
549                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
550                         ch_free( pm_prev );
551                 }
552         }
553 #endif
554
555         if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
556                 ldap_pvt_thread_yield();
557                 TXN_CHECKPOINT( bdb->bi_dbenv,
558                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
559         }
560
561 done:
562         if( ltid != NULL ) {
563 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
564                 pm_list = LDAP_LIST_FIRST(&op->o_pm_list);
565                 while ( pm_list != NULL ) {
566                         LDAP_LIST_REMOVE ( pm_list, ps_link );
567                         pm_prev = pm_list;
568                         pm_list = LDAP_LIST_NEXT ( pm_list, ps_link );
569                         ch_free( pm_prev );
570                 }
571 #endif
572                 TXN_ABORT( ltid );
573                 op->o_private = NULL;
574         }
575
576         if( e != NULL ) {
577                 bdb_unlocked_cache_return_entry_w (&bdb->bi_cache, e);
578         }
579         return ( ( rs->sr_err == LDAP_SUCCESS ) ? noop : rs->sr_err );
580 }