]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/delete.c
fix for concurrent persistent searches
[openldap] / servers / slapd / back-bdb / delete.c
1 /* delete.c - bdb backend delete routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2003 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
22 #include "back-bdb.h"
23 #include "external.h"
24
25 int
26 bdb_delete( Operation *op, SlapReply *rs )
27 {
28         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
29         Entry   *matched = NULL;
30         struct berval   pdn = {0, NULL};
31         Entry   *e = NULL;
32         Entry   *p = NULL;
33         EntryInfo       *ei = NULL, *eip = NULL;
34         int             manageDSAit = get_manageDSAit( op );
35         AttributeDescription *children = slap_schema.si_ad_children;
36         AttributeDescription *entry = slap_schema.si_ad_entry;
37         DB_TXN          *ltid = NULL, *lt2;
38         struct bdb_op_info opinfo;
39
40         u_int32_t       locker = 0;
41         DB_LOCK         lock, plock;
42
43         int             noop = 0;
44
45         int             num_retries = 0;
46
47         Operation* ps_list;
48         int     rc;
49         EntryInfo   *suffix_ei;
50         Entry       *ctxcsn_e;
51         int         ctxcsn_added = 0;
52
53         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
54         int num_ctrls = 0;
55
56 #ifdef NEW_LOGGING
57         LDAP_LOG ( OPERATION, ARGS,  "==> bdb_delete: %s\n", op->o_req_dn.bv_val, 0, 0 );
58 #else
59         Debug( LDAP_DEBUG_ARGS, "==> bdb_delete: %s\n",
60                 op->o_req_dn.bv_val, 0, 0 );
61 #endif
62
63         if( 0 ) {
64 retry:  /* transaction retry */
65                 if( e != NULL ) {
66                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
67                         e = NULL;
68                 }
69 #ifdef NEW_LOGGING
70                 LDAP_LOG ( OPERATION, DETAIL1, 
71                         "==> bdb_delete: retrying...\n", 0, 0, 0 );
72 #else
73                 Debug( LDAP_DEBUG_TRACE, "==> bdb_delete: retrying...\n",
74                         0, 0, 0 );
75 #endif
76                 rs->sr_err = TXN_ABORT( ltid );
77                 ltid = NULL;
78                 op->o_private = NULL;
79                 op->o_do_not_cache = opinfo.boi_acl_cache;
80                 if( rs->sr_err != 0 ) {
81                         rs->sr_err = LDAP_OTHER;
82                         rs->sr_text = "internal error";
83                         goto return_results;
84                 }
85                 ldap_pvt_thread_yield();
86                 bdb_trans_backoff( ++num_retries );
87         }
88
89         /* begin transaction */
90         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
91                 bdb->bi_db_opflags );
92         rs->sr_text = NULL;
93         if( rs->sr_err != 0 ) {
94 #ifdef NEW_LOGGING
95                 LDAP_LOG ( OPERATION, ERR, 
96                         "==> bdb_delete: txn_begin failed: %s (%d)\n",
97                         db_strerror(rs->sr_err), rs->sr_err, 0 );
98 #else
99                 Debug( LDAP_DEBUG_TRACE,
100                         "bdb_delete: txn_begin failed: %s (%d)\n",
101                         db_strerror(rs->sr_err), rs->sr_err, 0 );
102 #endif
103                 rs->sr_err = LDAP_OTHER;
104                 rs->sr_text = "internal error";
105                 goto return_results;
106         }
107
108         locker = TXN_ID ( ltid );
109
110         opinfo.boi_bdb = op->o_bd;
111         opinfo.boi_txn = ltid;
112         opinfo.boi_locker = locker;
113         opinfo.boi_err = 0;
114         opinfo.boi_acl_cache = op->o_do_not_cache;
115         op->o_private = &opinfo;
116
117         if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
118                 dnParent( &op->o_req_ndn, &pdn );
119         }
120
121         /* get entry */
122         rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
123                 locker, &lock );
124
125         switch( rs->sr_err ) {
126         case 0:
127         case DB_NOTFOUND:
128                 break;
129         case DB_LOCK_DEADLOCK:
130         case DB_LOCK_NOTGRANTED:
131                 goto retry;
132         case LDAP_BUSY:
133                 rs->sr_text = "ldap server busy";
134                 goto return_results;
135         default:
136                 rs->sr_err = LDAP_OTHER;
137                 rs->sr_text = "internal error";
138                 goto return_results;
139         }
140
141         if ( rs->sr_err == 0 ) {
142                 e = ei->bei_e;
143                 eip = ei->bei_parent;
144                 bdb_cache_find_id( op, ltid, eip->bei_id, &eip,
145                         0, locker, &plock );
146         } else {
147                 matched = ei->bei_e;
148         }
149         if ( eip ) {
150                 p = eip->bei_e;
151         }
152
153         if ( pdn.bv_len != 0 ) {
154                 if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
155 #ifdef NEW_LOGGING
156                         LDAP_LOG ( OPERATION, DETAIL1, 
157                                 "<=- bdb_delete: parent does not exist\n", 0, 0, 0 );
158 #else
159                         Debug( LDAP_DEBUG_TRACE,
160                                 "<=- bdb_delete: parent does not exist\n",
161                                 0, 0, 0);
162 #endif
163                         rs->sr_err = LDAP_OTHER;
164                         rs->sr_text = "could not locate parent of entry";
165                         goto return_results;
166                 }
167
168                 /* check parent for "children" acl */
169                 rs->sr_err = access_allowed( op, p,
170                         children, NULL, ACL_WRITE, NULL );
171
172                 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
173                 p = NULL;
174
175                 if ( !rs->sr_err  ) {
176                         switch( opinfo.boi_err ) {
177                         case DB_LOCK_DEADLOCK:
178                         case DB_LOCK_NOTGRANTED:
179                                 goto retry;
180                         }
181
182 #ifdef NEW_LOGGING
183                         LDAP_LOG ( OPERATION, DETAIL1, 
184                                 "<=- bdb_delete: no write access to parent\n", 0, 0, 0 );
185 #else
186                         Debug( LDAP_DEBUG_TRACE,
187                                 "<=- bdb_delete: no write access to parent\n",
188                                 0, 0, 0 );
189 #endif
190                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
191                         rs->sr_text = "no write access to parent";
192                         goto return_results;
193                 }
194
195         } else {
196                 /* no parent, must be root to delete */
197                 if( ! be_isroot( op->o_bd, &op->o_ndn ) ) {
198                         if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
199                                 || be_isupdate( op->o_bd, &op->o_ndn ) ) {
200                                 p = (Entry *)&slap_entry_root;
201
202                                 /* check parent for "children" acl */
203                                 rs->sr_err = access_allowed( op, p,
204                                         children, NULL, ACL_WRITE, NULL );
205
206                                 p = NULL;
207
208                                 if ( !rs->sr_err  ) {
209                                         switch( opinfo.boi_err ) {
210                                         case DB_LOCK_DEADLOCK:
211                                         case DB_LOCK_NOTGRANTED:
212                                                 goto retry;
213                                         }
214
215 #ifdef NEW_LOGGING
216                                         LDAP_LOG ( OPERATION, DETAIL1, 
217                                                 "<=- bdb_delete: no access to parent\n", 0, 0, 0 );
218 #else
219                                         Debug( LDAP_DEBUG_TRACE,
220                                                 "<=- bdb_delete: no access "
221                                                 "to parent\n", 0, 0, 0 );
222 #endif
223                                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
224                                         rs->sr_text = "no write access to parent";
225                                         goto return_results;
226                                 }
227
228                         } else {
229 #ifdef NEW_LOGGING
230                                 LDAP_LOG ( OPERATION, DETAIL1, 
231                                         "<=- bdb_delete: no parent and not root\n", 0, 0, 0 );
232 #else
233                                 Debug( LDAP_DEBUG_TRACE,
234                                         "<=- bdb_delete: no parent "
235                                         "and not root\n", 0, 0, 0);
236 #endif
237                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
238                                 goto return_results;
239                         }
240                 }
241         }
242
243         /* FIXME : dn2entry() should return non-glue entry */
244         if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
245 #ifdef NEW_LOGGING
246                 LDAP_LOG ( OPERATION, ARGS, 
247                         "<=- bdb_delete: no such object %s\n", op->o_req_dn.bv_val, 0, 0);
248 #else
249                 Debug( LDAP_DEBUG_ARGS,
250                         "<=- bdb_delete: no such object %s\n",
251                         op->o_req_dn.bv_val, 0, 0);
252 #endif
253
254                 if ( matched != NULL ) {
255                         rs->sr_matched = ch_strdup( matched->e_dn );
256                         rs->sr_ref = is_entry_referral( matched )
257                                 ? get_entry_referrals( op, matched )
258                                 : NULL;
259                         bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
260                         matched = NULL;
261
262                 } else {
263                         BerVarray deref = NULL;
264                         if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
265                                 syncinfo_t *si;
266                                 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
267                                         struct berval tmpbv;
268                                         ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
269                                         ber_bvarray_add( &deref, &tmpbv );
270                                 }
271                         } else {
272                                 deref = default_referral;
273                         }
274                         rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
275                                 LDAP_SCOPE_DEFAULT );
276                 }
277
278                 rs->sr_err = LDAP_REFERRAL;
279                 send_ldap_result( op, rs );
280
281                 if ( rs->sr_ref != default_referral ) {
282                         ber_bvarray_free( rs->sr_ref );
283                 }
284                 free( (char *)rs->sr_matched );
285                 rs->sr_ref = NULL;
286                 rs->sr_matched = NULL;
287
288                 rs->sr_err = -1;
289                 goto done;
290         }
291
292         if ( get_assert( op ) &&
293                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
294         {
295                 rs->sr_err = LDAP_ASSERTION_FAILED;
296                 goto return_results;
297         }
298
299         rs->sr_err = access_allowed( op, e,
300                 entry, NULL, ACL_WRITE, NULL );
301
302         if ( !rs->sr_err  ) {
303                 switch( opinfo.boi_err ) {
304                 case DB_LOCK_DEADLOCK:
305                 case DB_LOCK_NOTGRANTED:
306                         goto retry;
307                 }
308
309 #ifdef NEW_LOGGING
310                 LDAP_LOG ( OPERATION, DETAIL1, 
311                         "<=- bdb_delete: no write access to entry\n", 0, 0, 0 );
312 #else
313                 Debug( LDAP_DEBUG_TRACE,
314                         "<=- bdb_delete: no write access to entry\n",
315                         0, 0, 0 );
316 #endif
317                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
318                 rs->sr_text = "no write access to entry";
319                 goto return_results;
320         }
321
322         if ( !manageDSAit && is_entry_referral( e ) ) {
323                 /* entry is a referral, don't allow delete */
324                 rs->sr_ref = get_entry_referrals( op, e );
325
326 #ifdef NEW_LOGGING
327                 LDAP_LOG ( OPERATION, DETAIL1, 
328                         "<=- bdb_delete: entry is referral\n", 0, 0, 0 );
329 #else
330                 Debug( LDAP_DEBUG_TRACE,
331                         "bdb_delete: entry is referral\n", 0, 0, 0 );
332 #endif
333
334                 rs->sr_err = LDAP_REFERRAL;
335                 rs->sr_matched = e->e_name.bv_val;
336                 send_ldap_result( op, rs );
337
338                 ber_bvarray_free( rs->sr_ref );
339                 rs->sr_ref = NULL;
340                 rs->sr_matched = NULL;
341
342                 rs->sr_err = 1;
343                 goto done;
344         }
345
346         /* pre-read */
347         if( op->o_preread ) {
348                 if( slap_read_controls( op, rs, e,
349                         &slap_pre_read_bv, &ctrls[num_ctrls] ) )
350                 {
351 #ifdef NEW_LOGGING
352                         LDAP_LOG ( OPERATION, DETAIL1, 
353                                 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
354 #else
355                         Debug( LDAP_DEBUG_TRACE,
356                                 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
357 #endif
358                         goto return_results;
359                 }
360                 ctrls[++num_ctrls] = NULL;
361         }
362
363         /* nested transaction */
364         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
365                 bdb->bi_db_opflags );
366         rs->sr_text = NULL;
367         if( rs->sr_err != 0 ) {
368 #ifdef NEW_LOGGING
369                 LDAP_LOG ( OPERATION, ERR, 
370                         "bdb_delete: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
371 #else
372                 Debug( LDAP_DEBUG_TRACE,
373                         "bdb_delete: txn_begin(2) failed: %s (%d)\n",
374                         db_strerror(rs->sr_err), rs->sr_err, 0 );
375 #endif
376                 rs->sr_err = LDAP_OTHER;
377                 rs->sr_text = "internal error";
378                 goto return_results;
379         }
380
381         /* Can't do it if we have kids */
382         rs->sr_err = bdb_cache_children( op, lt2, e );
383         if( rs->sr_err != DB_NOTFOUND ) {
384                 switch( rs->sr_err ) {
385                 case DB_LOCK_DEADLOCK:
386                 case DB_LOCK_NOTGRANTED:
387                         goto retry;
388                 case 0:
389 #ifdef NEW_LOGGING
390                         LDAP_LOG ( OPERATION, DETAIL1, 
391                                 "<=- bdb_delete: non-leaf %s\n", op->o_req_dn.bv_val, 0, 0 );
392 #else
393                         Debug(LDAP_DEBUG_ARGS,
394                                 "<=- bdb_delete: non-leaf %s\n",
395                                 op->o_req_dn.bv_val, 0, 0);
396 #endif
397                         rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
398                         rs->sr_text = "subtree delete not supported";
399                         break;
400                 default:
401 #ifdef NEW_LOGGING
402                         LDAP_LOG ( OPERATION, ERR, 
403                                 "<=- bdb_delete: has_children failed %s (%d)\n",
404                                 db_strerror(rs->sr_err), rs->sr_err, 0 );
405 #else
406                         Debug(LDAP_DEBUG_ARGS,
407                                 "<=- bdb_delete: has_children failed: %s (%d)\n",
408                                 db_strerror(rs->sr_err), rs->sr_err, 0 );
409 #endif
410                         rs->sr_err = LDAP_OTHER;
411                         rs->sr_text = "internal error";
412                 }
413                 goto return_results;
414         }
415
416         /* delete from dn2id */
417         rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
418         if ( rs->sr_err != 0 ) {
419 #ifdef NEW_LOGGING
420                 LDAP_LOG ( OPERATION, ERR, 
421                         "<=- bdb_delete: dn2id failed: %s (%d)\n",
422                         db_strerror(rs->sr_err), rs->sr_err, 0 );
423 #else
424                 Debug(LDAP_DEBUG_TRACE,
425                         "<=- bdb_delete: dn2id failed: %s (%d)\n",
426                         db_strerror(rs->sr_err), rs->sr_err, 0 );
427 #endif
428                 switch( rs->sr_err ) {
429                 case DB_LOCK_DEADLOCK:
430                 case DB_LOCK_NOTGRANTED:
431                         goto retry;
432                 }
433                 rs->sr_text = "DN index delete failed";
434                 rs->sr_err = LDAP_OTHER;
435                 goto return_results;
436         }
437
438         /* delete from id2entry */
439         rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
440         if ( rs->sr_err != 0 ) {
441 #ifdef NEW_LOGGING
442                 LDAP_LOG ( OPERATION, ERR, 
443                         "<=- bdb_delete: id2entry failed: %s (%d)\n", 
444                         db_strerror(rs->sr_err), rs->sr_err, 0 );
445 #else
446                 Debug(LDAP_DEBUG_TRACE,
447                         "<=- bdb_delete: id2entry failed: %s (%d)\n",
448                         db_strerror(rs->sr_err), rs->sr_err, 0 );
449 #endif
450                 switch( rs->sr_err ) {
451                 case DB_LOCK_DEADLOCK:
452                 case DB_LOCK_NOTGRANTED:
453                         goto retry;
454                 }
455                 rs->sr_text = "entry delete failed";
456                 rs->sr_err = LDAP_OTHER;
457                 goto return_results;
458         }
459
460         /* delete indices for old attributes */
461         rs->sr_err = bdb_index_entry_del( op, lt2, e );
462         if ( rs->sr_err != LDAP_SUCCESS ) {
463 #ifdef NEW_LOGGING
464                 LDAP_LOG ( OPERATION, ERR, 
465                         "<=- bdb_delete: index failed: %s (%d)\n", 
466                         db_strerror(rs->sr_err), rs->sr_err, 0 );
467 #else
468                 Debug( LDAP_DEBUG_TRACE,
469                         "<=- bdb_delete: index failed: %s (%d)\n", 
470                         db_strerror(rs->sr_err), rs->sr_err, 0 );
471 #endif
472                 switch( rs->sr_err ) {
473                 case DB_LOCK_DEADLOCK:
474                 case DB_LOCK_NOTGRANTED:
475                         goto retry;
476                 }
477                 rs->sr_text = "entry index delete failed";
478                 rs->sr_err = LDAP_OTHER;
479                 goto return_results;
480         }
481         if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
482                 rs->sr_err = LDAP_OTHER;
483                 rs->sr_text = "txn_commit(2) failed";
484                 goto return_results;
485         }
486
487 #if 0   /* Do we want to reclaim deleted IDs? */
488         ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
489         if ( e->e_id == bdb->bi_lastid ) {
490                 bdb_last_id( op->o_bd, ltid );
491         }
492         ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
493 #endif
494
495         if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
496                 rc = bdb_csn_commit( op, rs, ltid, ei, &suffix_ei,
497                         &ctxcsn_e, &ctxcsn_added, locker );
498                 switch ( rc ) {
499                 case BDB_CSN_ABORT :
500                         goto return_results;
501                 case BDB_CSN_RETRY :
502                         goto retry;
503                 }
504         }
505
506         if( op->o_noop ) {
507                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
508                         rs->sr_text = "txn_abort (no-op) failed";
509                 } else {
510                         noop = 1;
511                         rs->sr_err = LDAP_SUCCESS;
512                 }
513         } else {
514                 bdb_cache_delete( &bdb->bi_cache, e, bdb->bi_dbenv,
515                         locker, &lock );
516
517                 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
518                         if ( ctxcsn_added ) {
519                                 bdb_cache_add( bdb, suffix_ei,
520                                         ctxcsn_e, (struct berval *)&slap_ldapsync_cn_bv, locker );
521                         }
522                 }
523
524                 if ( rs->sr_err == LDAP_SUCCESS && !noop && !op->o_no_psearch ) {
525                         ldap_pvt_thread_rdwr_rlock( &bdb->bi_pslist_rwlock );
526                         LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
527                                 bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_DELETE );
528                         }
529                         ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
530                 }
531
532                 rs->sr_err = TXN_COMMIT( ltid, 0 );
533         }
534         ltid = NULL;
535         op->o_private = NULL;
536
537         if( rs->sr_err != 0 ) {
538 #ifdef NEW_LOGGING
539                 LDAP_LOG ( OPERATION, ERR, 
540                         "bdb_delete: txn_%s failed: %s (%d)\n",
541                         op->o_noop ? "abort (no-op)" : "commit", db_strerror(rs->sr_err), rs->sr_err );
542 #else
543                 Debug( LDAP_DEBUG_TRACE,
544                         "bdb_delete: txn_%s failed: %s (%d)\n",
545                         op->o_noop ? "abort (no-op)" : "commit",
546                         db_strerror(rs->sr_err), rs->sr_err );
547 #endif
548                 rs->sr_err = LDAP_OTHER;
549                 rs->sr_text = "commit failed";
550
551                 goto return_results;
552         }
553
554 #ifdef NEW_LOGGING
555         LDAP_LOG ( OPERATION, RESULTS, 
556                 "bdb_delete: deleted%s id=%08lx db=\"%s\"\n",
557                 op->o_noop ? " (no-op)" : "", e->e_id, e->e_dn );
558 #else
559         Debug( LDAP_DEBUG_TRACE,
560                 "bdb_delete: deleted%s id=%08lx dn=\"%s\"\n",
561                 op->o_noop ? " (no-op)" : "",
562                 e->e_id, e->e_dn );
563 #endif
564         rs->sr_err = LDAP_SUCCESS;
565         rs->sr_text = NULL;
566         if( num_ctrls ) rs->sr_ctrls = ctrls;
567
568 return_results:
569         send_ldap_result( op, rs );
570
571         if(rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
572                 ldap_pvt_thread_yield();
573                 TXN_CHECKPOINT( bdb->bi_dbenv,
574                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
575         }
576
577 done:
578         /* free entry */
579         if( e != NULL ) {
580                 if ( rs->sr_err == LDAP_SUCCESS ) {
581                         /* Free the EntryInfo and the Entry */
582                         bdb_cache_delete_cleanup( &bdb->bi_cache, e );
583                 } else {
584                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
585                 }
586         }
587
588         if( ltid != NULL ) {
589                 TXN_ABORT( ltid );
590                 op->o_private = NULL;
591         }
592
593         return ( ( rs->sr_err == LDAP_SUCCESS ) ? noop : rs->sr_err );
594 }