1 /* delete.c - bdb backend delete routine */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2004 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
20 #include <ac/string.h>
26 bdb_delete( Operation *op, SlapReply *rs )
28 struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
29 Entry *matched = NULL;
30 struct berval pdn = {0, 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;
51 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
55 LDAP_LOG ( OPERATION, ARGS, "==> bdb_delete: %s\n", op->o_req_dn.bv_val, 0, 0 );
57 Debug( LDAP_DEBUG_ARGS, "==> bdb_delete: %s\n",
58 op->o_req_dn.bv_val, 0, 0 );
62 retry: /* transaction retry */
64 bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
68 LDAP_LOG ( OPERATION, DETAIL1,
69 "==> bdb_delete: retrying...\n", 0, 0, 0 );
71 Debug( LDAP_DEBUG_TRACE, "==> bdb_delete: retrying...\n",
74 rs->sr_err = TXN_ABORT( ltid );
77 op->o_do_not_cache = opinfo.boi_acl_cache;
78 if( rs->sr_err != 0 ) {
79 rs->sr_err = LDAP_OTHER;
80 rs->sr_text = "internal error";
83 ldap_pvt_thread_yield();
84 bdb_trans_backoff( ++num_retries );
87 /* begin transaction */
88 rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, <id,
91 if( rs->sr_err != 0 ) {
93 LDAP_LOG ( OPERATION, ERR,
94 "==> bdb_delete: txn_begin failed: %s (%d)\n",
95 db_strerror(rs->sr_err), rs->sr_err, 0 );
97 Debug( LDAP_DEBUG_TRACE,
98 "bdb_delete: txn_begin failed: %s (%d)\n",
99 db_strerror(rs->sr_err), rs->sr_err, 0 );
101 rs->sr_err = LDAP_OTHER;
102 rs->sr_text = "internal error";
106 locker = TXN_ID ( ltid );
108 opinfo.boi_bdb = op->o_bd;
109 opinfo.boi_txn = ltid;
110 opinfo.boi_locker = locker;
112 opinfo.boi_acl_cache = op->o_do_not_cache;
113 op->o_private = &opinfo;
115 if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
116 dnParent( &op->o_req_ndn, &pdn );
120 rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
123 switch( rs->sr_err ) {
127 case DB_LOCK_DEADLOCK:
128 case DB_LOCK_NOTGRANTED:
131 rs->sr_text = "ldap server busy";
134 rs->sr_err = LDAP_OTHER;
135 rs->sr_text = "internal error";
139 if ( rs->sr_err == 0 ) {
141 eip = ei->bei_parent;
146 /* FIXME : dn2entry() should return non-glue entry */
147 if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
149 LDAP_LOG ( OPERATION, ARGS,
150 "<=- bdb_delete: no such object %s\n", op->o_req_dn.bv_val, 0, 0);
152 Debug( LDAP_DEBUG_ARGS,
153 "<=- bdb_delete: no such object %s\n",
154 op->o_req_dn.bv_val, 0, 0);
157 if ( matched != NULL ) {
158 rs->sr_matched = ch_strdup( matched->e_dn );
159 rs->sr_ref = is_entry_referral( matched )
160 ? get_entry_referrals( op, matched )
162 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
166 BerVarray deref = NULL;
167 if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
169 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
171 ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
172 ber_bvarray_add( &deref, &tmpbv );
175 deref = default_referral;
177 rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
178 LDAP_SCOPE_DEFAULT );
181 rs->sr_err = LDAP_REFERRAL;
182 send_ldap_result( op, rs );
184 if ( rs->sr_ref != default_referral ) {
185 ber_bvarray_free( rs->sr_ref );
187 free( (char *)rs->sr_matched );
189 rs->sr_matched = NULL;
195 bdb_cache_find_id( op, ltid, eip->bei_id, &eip, 0, locker, &plock );
196 if ( eip ) p = eip->bei_e;
198 if ( pdn.bv_len != 0 ) {
199 if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
201 LDAP_LOG ( OPERATION, DETAIL1,
202 "<=- bdb_delete: parent does not exist\n", 0, 0, 0 );
204 Debug( LDAP_DEBUG_TRACE,
205 "<=- bdb_delete: parent does not exist\n",
208 rs->sr_err = LDAP_OTHER;
209 rs->sr_text = "could not locate parent of entry";
213 /* check parent for "children" acl */
214 rs->sr_err = access_allowed( op, p,
215 children, NULL, ACL_WRITE, NULL );
217 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
221 switch( opinfo.boi_err ) {
222 case DB_LOCK_DEADLOCK:
223 case DB_LOCK_NOTGRANTED:
228 LDAP_LOG ( OPERATION, DETAIL1,
229 "<=- bdb_delete: no write access to parent\n", 0, 0, 0 );
231 Debug( LDAP_DEBUG_TRACE,
232 "<=- bdb_delete: no write access to parent\n",
235 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
236 rs->sr_text = "no write access to parent";
241 /* no parent, must be root to delete */
242 if( ! be_isroot( op->o_bd, &op->o_ndn ) ) {
243 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
244 || be_isupdate( op->o_bd, &op->o_ndn ) ) {
245 p = (Entry *)&slap_entry_root;
247 /* check parent for "children" acl */
248 rs->sr_err = access_allowed( op, p,
249 children, NULL, ACL_WRITE, NULL );
254 switch( opinfo.boi_err ) {
255 case DB_LOCK_DEADLOCK:
256 case DB_LOCK_NOTGRANTED:
261 LDAP_LOG ( OPERATION, DETAIL1,
262 "<=- bdb_delete: no access to parent\n", 0, 0, 0 );
264 Debug( LDAP_DEBUG_TRACE,
265 "<=- bdb_delete: no access "
266 "to parent\n", 0, 0, 0 );
268 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
269 rs->sr_text = "no write access to parent";
275 LDAP_LOG ( OPERATION, DETAIL1,
276 "<=- bdb_delete: no parent and not root\n", 0, 0, 0 );
278 Debug( LDAP_DEBUG_TRACE,
279 "<=- bdb_delete: no parent "
280 "and not root\n", 0, 0, 0);
282 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
288 if ( get_assert( op ) &&
289 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
291 rs->sr_err = LDAP_ASSERTION_FAILED;
295 rs->sr_err = access_allowed( op, e,
296 entry, NULL, ACL_WRITE, NULL );
299 switch( opinfo.boi_err ) {
300 case DB_LOCK_DEADLOCK:
301 case DB_LOCK_NOTGRANTED:
306 LDAP_LOG ( OPERATION, DETAIL1,
307 "<=- bdb_delete: no write access to entry\n", 0, 0, 0 );
309 Debug( LDAP_DEBUG_TRACE,
310 "<=- bdb_delete: no write access to entry\n",
313 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
314 rs->sr_text = "no write access to entry";
318 if ( !manageDSAit && is_entry_referral( e ) ) {
319 /* entry is a referral, don't allow delete */
320 rs->sr_ref = get_entry_referrals( op, e );
323 LDAP_LOG ( OPERATION, DETAIL1,
324 "<=- bdb_delete: entry is referral\n", 0, 0, 0 );
326 Debug( LDAP_DEBUG_TRACE,
327 "bdb_delete: entry is referral\n", 0, 0, 0 );
330 rs->sr_err = LDAP_REFERRAL;
331 rs->sr_matched = e->e_name.bv_val;
332 send_ldap_result( op, rs );
334 ber_bvarray_free( rs->sr_ref );
336 rs->sr_matched = NULL;
343 if( op->o_preread ) {
344 if( slap_read_controls( op, rs, e,
345 &slap_pre_read_bv, &ctrls[num_ctrls] ) )
348 LDAP_LOG ( OPERATION, DETAIL1,
349 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
351 Debug( LDAP_DEBUG_TRACE,
352 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
356 ctrls[++num_ctrls] = NULL;
359 /* nested transaction */
360 rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2,
361 bdb->bi_db_opflags );
363 if( rs->sr_err != 0 ) {
365 LDAP_LOG ( OPERATION, ERR,
366 "bdb_delete: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
368 Debug( LDAP_DEBUG_TRACE,
369 "bdb_delete: txn_begin(2) failed: %s (%d)\n",
370 db_strerror(rs->sr_err), rs->sr_err, 0 );
372 rs->sr_err = LDAP_OTHER;
373 rs->sr_text = "internal error";
377 /* Can't do it if we have kids */
378 rs->sr_err = bdb_cache_children( op, lt2, e );
379 if( rs->sr_err != DB_NOTFOUND ) {
380 switch( rs->sr_err ) {
381 case DB_LOCK_DEADLOCK:
382 case DB_LOCK_NOTGRANTED:
386 LDAP_LOG ( OPERATION, DETAIL1,
387 "<=- bdb_delete: non-leaf %s\n", op->o_req_dn.bv_val, 0, 0 );
389 Debug(LDAP_DEBUG_ARGS,
390 "<=- bdb_delete: non-leaf %s\n",
391 op->o_req_dn.bv_val, 0, 0);
393 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
394 rs->sr_text = "subtree delete not supported";
398 LDAP_LOG ( OPERATION, ERR,
399 "<=- bdb_delete: has_children failed %s (%d)\n",
400 db_strerror(rs->sr_err), rs->sr_err, 0 );
402 Debug(LDAP_DEBUG_ARGS,
403 "<=- bdb_delete: has_children failed: %s (%d)\n",
404 db_strerror(rs->sr_err), rs->sr_err, 0 );
406 rs->sr_err = LDAP_OTHER;
407 rs->sr_text = "internal error";
412 /* delete from dn2id */
413 rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
414 if ( rs->sr_err != 0 ) {
416 LDAP_LOG ( OPERATION, ERR,
417 "<=- bdb_delete: dn2id failed: %s (%d)\n",
418 db_strerror(rs->sr_err), rs->sr_err, 0 );
420 Debug(LDAP_DEBUG_TRACE,
421 "<=- bdb_delete: dn2id failed: %s (%d)\n",
422 db_strerror(rs->sr_err), rs->sr_err, 0 );
424 switch( rs->sr_err ) {
425 case DB_LOCK_DEADLOCK:
426 case DB_LOCK_NOTGRANTED:
429 rs->sr_text = "DN index delete failed";
430 rs->sr_err = LDAP_OTHER;
434 /* delete from id2entry */
435 rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
436 if ( rs->sr_err != 0 ) {
438 LDAP_LOG ( OPERATION, ERR,
439 "<=- bdb_delete: id2entry failed: %s (%d)\n",
440 db_strerror(rs->sr_err), rs->sr_err, 0 );
442 Debug(LDAP_DEBUG_TRACE,
443 "<=- bdb_delete: id2entry failed: %s (%d)\n",
444 db_strerror(rs->sr_err), rs->sr_err, 0 );
446 switch( rs->sr_err ) {
447 case DB_LOCK_DEADLOCK:
448 case DB_LOCK_NOTGRANTED:
451 rs->sr_text = "entry delete failed";
452 rs->sr_err = LDAP_OTHER;
456 /* delete indices for old attributes */
457 rs->sr_err = bdb_index_entry_del( op, lt2, e );
458 if ( rs->sr_err != LDAP_SUCCESS ) {
460 LDAP_LOG ( OPERATION, ERR,
461 "<=- bdb_delete: index failed: %s (%d)\n",
462 db_strerror(rs->sr_err), rs->sr_err, 0 );
464 Debug( LDAP_DEBUG_TRACE,
465 "<=- bdb_delete: index failed: %s (%d)\n",
466 db_strerror(rs->sr_err), rs->sr_err, 0 );
468 switch( rs->sr_err ) {
469 case DB_LOCK_DEADLOCK:
470 case DB_LOCK_NOTGRANTED:
473 rs->sr_text = "entry index delete failed";
474 rs->sr_err = LDAP_OTHER;
477 if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
478 rs->sr_err = LDAP_OTHER;
479 rs->sr_text = "txn_commit(2) failed";
483 #if 0 /* Do we want to reclaim deleted IDs? */
484 ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
485 if ( e->e_id == bdb->bi_lastid ) {
486 bdb_last_id( op->o_bd, ltid );
488 ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
491 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
492 rc = bdb_csn_commit( op, rs, ltid, ei, &suffix_ei,
493 &ctxcsn_e, &ctxcsn_added, locker );
503 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
504 rs->sr_text = "txn_abort (no-op) failed";
506 rs->sr_err = LDAP_NO_OPERATION;
510 bdb_cache_delete( &bdb->bi_cache, e, bdb->bi_dbenv,
513 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
514 if ( ctxcsn_added ) {
515 bdb_cache_add( bdb, suffix_ei,
516 ctxcsn_e, (struct berval *)&slap_ldapsync_cn_bv, locker );
520 if ( rs->sr_err == LDAP_SUCCESS && !op->o_no_psearch ) {
521 ldap_pvt_thread_rdwr_rlock( &bdb->bi_pslist_rwlock );
522 LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
523 bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_DELETE );
525 ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
528 rs->sr_err = TXN_COMMIT( ltid, 0 );
531 op->o_private = NULL;
533 if( rs->sr_err != 0 ) {
535 LDAP_LOG ( OPERATION, ERR,
536 "bdb_delete: txn_%s failed: %s (%d)\n",
537 op->o_noop ? "abort (no-op)" : "commit",
538 db_strerror(rs->sr_err), rs->sr_err );
540 Debug( LDAP_DEBUG_TRACE,
541 "bdb_delete: txn_%s failed: %s (%d)\n",
542 op->o_noop ? "abort (no-op)" : "commit",
543 db_strerror(rs->sr_err), rs->sr_err );
545 rs->sr_err = LDAP_OTHER;
546 rs->sr_text = "commit failed";
552 LDAP_LOG ( OPERATION, RESULTS,
553 "bdb_delete: deleted%s id=%08lx db=\"%s\"\n",
554 op->o_noop ? " (no-op)" : "",
557 Debug( LDAP_DEBUG_TRACE,
558 "bdb_delete: deleted%s id=%08lx dn=\"%s\"\n",
559 op->o_noop ? " (no-op)" : "",
562 rs->sr_err = LDAP_SUCCESS;
564 if( num_ctrls ) rs->sr_ctrls = ctrls;
567 send_ldap_result( op, rs );
569 if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
570 ldap_pvt_thread_yield();
571 TXN_CHECKPOINT( bdb->bi_dbenv,
572 bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
578 if ( rs->sr_err == LDAP_SUCCESS ) {
579 /* Free the EntryInfo and the Entry */
580 bdb_cache_delete_cleanup( &bdb->bi_cache, e );
582 bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
588 op->o_private = NULL;