1 /* delete.c - bdb backend delete routine */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/back-bdb/delete.c,v 1.155.2.8 2008/05/01 21:39:35 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2008 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
= {0};
41 BDB_LOCKER locker
= 0;
48 LDAPControl
**preread_ctrl
= NULL
;
49 LDAPControl
*ctrls
[SLAP_MAX_RESPONSE_CONTROLS
];
52 int parent_is_glue
= 0;
53 int parent_is_leaf
= 0;
59 Debug( LDAP_DEBUG_ARGS
, "==> " LDAP_XSTRING(bdb_delete
) ": %s\n",
60 op
->o_req_dn
.bv_val
, 0, 0 );
64 /* acquire connection lock */
65 ldap_pvt_thread_mutex_lock( &op
->o_conn
->c_mutex
);
66 if( op
->o_conn
->c_txn
== CONN_TXN_INACTIVE
) {
67 rs
->sr_text
= "invalid transaction identifier";
68 rs
->sr_err
= LDAP_X_TXN_ID_INVALID
;
70 } else if( op
->o_conn
->c_txn
== CONN_TXN_SETTLE
) {
75 if( op
->o_conn
->c_txn_backend
== NULL
) {
76 op
->o_conn
->c_txn_backend
= op
->o_bd
;
78 } else if( op
->o_conn
->c_txn_backend
!= op
->o_bd
) {
79 rs
->sr_text
= "transaction cannot span multiple database contexts";
80 rs
->sr_err
= LDAP_AFFECTS_MULTIPLE_DSAS
;
84 /* insert operation into transaction */
86 rs
->sr_text
= "transaction specified";
87 rs
->sr_err
= LDAP_X_TXN_SPECIFY_OKAY
;
90 /* release connection lock */
91 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
94 send_ldap_result( op
, rs
);
100 ctrls
[num_ctrls
] = 0;
103 if ( BER_BVISNULL( &op
->o_csn
) ) {
105 char csnbuf
[LDAP_LUTIL_CSNSTR_BUFSIZE
];
108 csn
.bv_len
= sizeof(csnbuf
);
109 slap_get_csn( op
, &csn
, 1 );
113 retry
: /* transaction retry */
115 bdb_unlocked_cache_return_entry_w(&bdb
->bi_cache
, e
);
119 bdb_unlocked_cache_return_entry_r(&bdb
->bi_cache
, p
);
122 Debug( LDAP_DEBUG_TRACE
,
123 "==> " LDAP_XSTRING(bdb_delete
) ": retrying...\n",
125 rs
->sr_err
= TXN_ABORT( ltid
);
127 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
128 opinfo
.boi_oe
.oe_key
= NULL
;
129 op
->o_do_not_cache
= opinfo
.boi_acl_cache
;
130 if( rs
->sr_err
!= 0 ) {
131 rs
->sr_err
= LDAP_OTHER
;
132 rs
->sr_text
= "internal error";
135 if ( op
->o_abandon
) {
136 rs
->sr_err
= SLAPD_ABANDON
;
141 bdb_trans_backoff( ++num_retries
);
144 /* begin transaction */
145 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, NULL
, <id
,
146 bdb
->bi_db_opflags
);
148 if( rs
->sr_err
!= 0 ) {
149 Debug( LDAP_DEBUG_TRACE
,
150 LDAP_XSTRING(bdb_delete
) ": txn_begin failed: "
151 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
152 rs
->sr_err
= LDAP_OTHER
;
153 rs
->sr_text
= "internal error";
157 locker
= TXN_ID ( ltid
);
159 opinfo
.boi_oe
.oe_key
= bdb
;
160 opinfo
.boi_txn
= ltid
;
162 opinfo
.boi_acl_cache
= op
->o_do_not_cache
;
163 LDAP_SLIST_INSERT_HEAD( &op
->o_extra
, &opinfo
.boi_oe
, oe_next
);
165 if ( !be_issuffix( op
->o_bd
, &op
->o_req_ndn
) ) {
166 dnParent( &op
->o_req_ndn
, &pdn
);
170 rs
->sr_err
= bdb_dn2entry( op
, ltid
, &op
->o_req_ndn
, &ei
, 1,
173 switch( rs
->sr_err
) {
177 case DB_LOCK_DEADLOCK
:
178 case DB_LOCK_NOTGRANTED
:
181 rs
->sr_text
= "ldap server busy";
184 rs
->sr_err
= LDAP_OTHER
;
185 rs
->sr_text
= "internal error";
189 if ( rs
->sr_err
== 0 ) {
191 eip
= ei
->bei_parent
;
196 /* FIXME : dn2entry() should return non-glue entry */
197 if ( e
== NULL
|| ( !manageDSAit
&& is_entry_glue( e
))) {
198 Debug( LDAP_DEBUG_ARGS
,
199 "<=- " LDAP_XSTRING(bdb_delete
) ": no such object %s\n",
200 op
->o_req_dn
.bv_val
, 0, 0);
202 if ( matched
!= NULL
) {
203 rs
->sr_matched
= ch_strdup( matched
->e_dn
);
204 rs
->sr_ref
= is_entry_referral( matched
)
205 ? get_entry_referrals( op
, matched
)
207 bdb_unlocked_cache_return_entry_r(&bdb
->bi_cache
, matched
);
211 rs
->sr_ref
= referral_rewrite( default_referral
, NULL
,
212 &op
->o_req_dn
, LDAP_SCOPE_DEFAULT
);
215 rs
->sr_err
= LDAP_REFERRAL
;
216 rs
->sr_flags
= REP_MATCHED_MUSTBEFREED
| REP_REF_MUSTBEFREED
;
220 rc
= bdb_cache_find_id( op
, ltid
, eip
->bei_id
, &eip
, 0, locker
, &plock
);
222 case DB_LOCK_DEADLOCK
:
223 case DB_LOCK_NOTGRANTED
:
229 rs
->sr_err
= LDAP_OTHER
;
230 rs
->sr_text
= "internal error";
233 if ( eip
) p
= eip
->bei_e
;
235 if ( pdn
.bv_len
!= 0 ) {
236 if( p
== NULL
|| !bvmatch( &pdn
, &p
->e_nname
)) {
237 Debug( LDAP_DEBUG_TRACE
,
238 "<=- " LDAP_XSTRING(bdb_delete
) ": parent "
239 "does not exist\n", 0, 0, 0 );
240 rs
->sr_err
= LDAP_OTHER
;
241 rs
->sr_text
= "could not locate parent of entry";
245 /* check parent for "children" acl */
246 rs
->sr_err
= access_allowed( op
, p
,
247 children
, NULL
, ACL_WDEL
, NULL
);
250 switch( opinfo
.boi_err
) {
251 case DB_LOCK_DEADLOCK
:
252 case DB_LOCK_NOTGRANTED
:
256 Debug( LDAP_DEBUG_TRACE
,
257 "<=- " LDAP_XSTRING(bdb_delete
) ": no write "
258 "access to parent\n", 0, 0, 0 );
259 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
260 rs
->sr_text
= "no write access to parent";
265 /* no parent, must be root to delete */
266 if( ! be_isroot( op
) ) {
267 if ( be_issuffix( op
->o_bd
, (struct berval
*)&slap_empty_bv
)
268 || be_shadow_update( op
) ) {
269 p
= (Entry
*)&slap_entry_root
;
271 /* check parent for "children" acl */
272 rs
->sr_err
= access_allowed( op
, p
,
273 children
, NULL
, ACL_WDEL
, NULL
);
278 switch( opinfo
.boi_err
) {
279 case DB_LOCK_DEADLOCK
:
280 case DB_LOCK_NOTGRANTED
:
284 Debug( LDAP_DEBUG_TRACE
,
285 "<=- " LDAP_XSTRING(bdb_delete
)
286 ": no access to parent\n",
288 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
289 rs
->sr_text
= "no write access to parent";
294 Debug( LDAP_DEBUG_TRACE
,
295 "<=- " LDAP_XSTRING(bdb_delete
)
296 ": no parent and not root\n", 0, 0, 0 );
297 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
303 if ( get_assert( op
) &&
304 ( test_filter( op
, e
, get_assertion( op
)) != LDAP_COMPARE_TRUE
))
306 rs
->sr_err
= LDAP_ASSERTION_FAILED
;
310 rs
->sr_err
= access_allowed( op
, e
,
311 entry
, NULL
, ACL_WDEL
, NULL
);
314 switch( opinfo
.boi_err
) {
315 case DB_LOCK_DEADLOCK
:
316 case DB_LOCK_NOTGRANTED
:
320 Debug( LDAP_DEBUG_TRACE
,
321 "<=- " LDAP_XSTRING(bdb_delete
) ": no write access "
322 "to entry\n", 0, 0, 0 );
323 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
324 rs
->sr_text
= "no write access to entry";
328 if ( !manageDSAit
&& is_entry_referral( e
) ) {
329 /* entry is a referral, don't allow delete */
330 rs
->sr_ref
= get_entry_referrals( op
, e
);
332 Debug( LDAP_DEBUG_TRACE
,
333 LDAP_XSTRING(bdb_delete
) ": entry is referral\n",
336 rs
->sr_err
= LDAP_REFERRAL
;
337 rs
->sr_matched
= ch_strdup( e
->e_name
.bv_val
);
338 rs
->sr_flags
= REP_MATCHED_MUSTBEFREED
| REP_REF_MUSTBEFREED
;
343 if( op
->o_preread
) {
344 if( preread_ctrl
== NULL
) {
345 preread_ctrl
= &ctrls
[num_ctrls
++];
346 ctrls
[num_ctrls
] = NULL
;
348 if( slap_read_controls( op
, rs
, e
,
349 &slap_pre_read_bv
, preread_ctrl
) )
351 Debug( LDAP_DEBUG_TRACE
,
352 "<=- " LDAP_XSTRING(bdb_delete
) ": pre-read "
353 "failed!\n", 0, 0, 0 );
354 if ( op
->o_preread
& SLAP_CONTROL_CRITICAL
) {
355 /* FIXME: is it correct to abort
356 * operation if control fails? */
362 /* nested transaction */
363 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, ltid
, <2
,
364 bdb
->bi_db_opflags
);
366 if( rs
->sr_err
!= 0 ) {
367 Debug( LDAP_DEBUG_TRACE
,
368 LDAP_XSTRING(bdb_delete
) ": txn_begin(2) failed: "
369 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
370 rs
->sr_err
= LDAP_OTHER
;
371 rs
->sr_text
= "internal error";
375 BDB_LOG_PRINTF( bdb
->bi_dbenv
, lt2
, "slapd Starting delete %s(%d)",
376 e
->e_nname
.bv_val
, e
->e_id
);
378 /* Can't do it if we have kids */
379 rs
->sr_err
= bdb_cache_children( op
, lt2
, e
);
380 if( rs
->sr_err
!= DB_NOTFOUND
) {
381 switch( rs
->sr_err
) {
382 case DB_LOCK_DEADLOCK
:
383 case DB_LOCK_NOTGRANTED
:
386 Debug(LDAP_DEBUG_ARGS
,
387 "<=- " LDAP_XSTRING(bdb_delete
)
389 op
->o_req_dn
.bv_val
, 0, 0);
390 rs
->sr_err
= LDAP_NOT_ALLOWED_ON_NONLEAF
;
391 rs
->sr_text
= "subordinate objects must be deleted first";
394 Debug(LDAP_DEBUG_ARGS
,
395 "<=- " LDAP_XSTRING(bdb_delete
)
396 ": has_children failed: %s (%d)\n",
397 db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
398 rs
->sr_err
= LDAP_OTHER
;
399 rs
->sr_text
= "internal error";
404 /* delete from dn2id */
405 rs
->sr_err
= bdb_dn2id_delete( op
, lt2
, eip
, e
);
406 if ( rs
->sr_err
!= 0 ) {
407 Debug(LDAP_DEBUG_TRACE
,
408 "<=- " LDAP_XSTRING(bdb_delete
) ": dn2id failed: "
409 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
410 switch( rs
->sr_err
) {
411 case DB_LOCK_DEADLOCK
:
412 case DB_LOCK_NOTGRANTED
:
415 rs
->sr_text
= "DN index delete failed";
416 rs
->sr_err
= LDAP_OTHER
;
420 /* delete indices for old attributes */
421 rs
->sr_err
= bdb_index_entry_del( op
, lt2
, e
);
422 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
423 Debug(LDAP_DEBUG_TRACE
,
424 "<=- " LDAP_XSTRING(bdb_delete
) ": index failed: "
425 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
426 switch( rs
->sr_err
) {
427 case DB_LOCK_DEADLOCK
:
428 case DB_LOCK_NOTGRANTED
:
431 rs
->sr_text
= "entry index delete failed";
432 rs
->sr_err
= LDAP_OTHER
;
436 /* fixup delete CSN */
437 if ( !SLAP_SHADOW( op
->o_bd
)) {
438 struct berval vals
[2];
440 assert( !BER_BVISNULL( &op
->o_csn
) );
442 BER_BVZERO( &vals
[1] );
443 rs
->sr_err
= bdb_index_values( op
, lt2
, slap_schema
.si_ad_entryCSN
,
444 vals
, 0, SLAP_INDEX_ADD_OP
);
445 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
446 switch( rs
->sr_err
) {
447 case DB_LOCK_DEADLOCK
:
448 case DB_LOCK_NOTGRANTED
:
451 rs
->sr_text
= "entryCSN index update failed";
452 rs
->sr_err
= LDAP_OTHER
;
457 /* delete from id2entry */
458 rs
->sr_err
= bdb_id2entry_delete( op
->o_bd
, lt2
, e
);
459 if ( rs
->sr_err
!= 0 ) {
460 Debug( LDAP_DEBUG_TRACE
,
461 "<=- " LDAP_XSTRING(bdb_delete
) ": id2entry failed: "
462 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
463 switch( rs
->sr_err
) {
464 case DB_LOCK_DEADLOCK
:
465 case DB_LOCK_NOTGRANTED
:
468 rs
->sr_text
= "entry delete failed";
469 rs
->sr_err
= LDAP_OTHER
;
473 if ( pdn
.bv_len
!= 0 ) {
474 parent_is_glue
= is_entry_glue(p
);
475 rs
->sr_err
= bdb_cache_children( op
, lt2
, p
);
476 if ( rs
->sr_err
!= DB_NOTFOUND
) {
477 switch( rs
->sr_err
) {
478 case DB_LOCK_DEADLOCK
:
479 case DB_LOCK_NOTGRANTED
:
484 Debug(LDAP_DEBUG_ARGS
,
485 "<=- " LDAP_XSTRING(bdb_delete
)
486 ": has_children failed: %s (%d)\n",
487 db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
488 rs
->sr_err
= LDAP_OTHER
;
489 rs
->sr_text
= "internal error";
494 bdb_unlocked_cache_return_entry_r(&bdb
->bi_cache
, p
);
498 BDB_LOG_PRINTF( bdb
->bi_dbenv
, lt2
, "slapd Commit1 delete %s(%d)",
499 e
->e_nname
.bv_val
, e
->e_id
);
501 if ( TXN_COMMIT( lt2
, 0 ) != 0 ) {
502 rs
->sr_err
= LDAP_OTHER
;
503 rs
->sr_text
= "txn_commit(2) failed";
509 #if 0 /* Do we want to reclaim deleted IDs? */
510 ldap_pvt_thread_mutex_lock( &bdb
->bi_lastid_mutex
);
511 if ( e
->e_id
== bdb
->bi_lastid
) {
512 bdb_last_id( op
->o_bd
, ltid
);
514 ldap_pvt_thread_mutex_unlock( &bdb
->bi_lastid_mutex
);
518 if ( ( rs
->sr_err
= TXN_ABORT( ltid
) ) != 0 ) {
519 rs
->sr_text
= "txn_abort (no-op) failed";
521 rs
->sr_err
= LDAP_X_NO_OPERATION
;
527 BDB_LOG_PRINTF( bdb
->bi_dbenv
, ltid
, "slapd Cache delete %s(%d)",
528 e
->e_nname
.bv_val
, e
->e_id
);
530 rc
= bdb_cache_delete( bdb
, e
, locker
, &lock
);
532 case DB_LOCK_DEADLOCK
:
533 case DB_LOCK_NOTGRANTED
:
537 rs
->sr_err
= TXN_COMMIT( ltid
, 0 );
540 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
541 opinfo
.boi_oe
.oe_key
= NULL
;
543 BDB_LOG_PRINTF( bdb
->bi_dbenv
, NULL
, "slapd Committed delete %s(%d)",
544 e
->e_nname
.bv_val
, e
->e_id
);
546 if( rs
->sr_err
!= 0 ) {
547 Debug( LDAP_DEBUG_TRACE
,
548 LDAP_XSTRING(bdb_delete
) ": txn_%s failed: %s (%d)\n",
549 op
->o_noop
? "abort (no-op)" : "commit",
550 db_strerror(rs
->sr_err
), rs
->sr_err
);
551 rs
->sr_err
= LDAP_OTHER
;
552 rs
->sr_text
= "commit failed";
557 Debug( LDAP_DEBUG_TRACE
,
558 LDAP_XSTRING(bdb_delete
) ": deleted%s id=%08lx dn=\"%s\"\n",
559 op
->o_noop
? " (no-op)" : "",
560 eid
, op
->o_req_dn
.bv_val
);
561 rs
->sr_err
= LDAP_SUCCESS
;
563 if( num_ctrls
) rs
->sr_ctrls
= ctrls
;
566 if ( rs
->sr_err
== LDAP_SUCCESS
&& parent_is_glue
&& parent_is_leaf
) {
567 op
->o_delete_glue_parent
= 1;
571 bdb_unlocked_cache_return_entry_r(&bdb
->bi_cache
, p
);
575 if ( rs
->sr_err
== LDAP_SUCCESS
) {
576 /* Free the EntryInfo and the Entry */
577 bdb_cache_entryinfo_lock( BEI(e
) );
578 bdb_cache_delete_cleanup( &bdb
->bi_cache
, BEI(e
) );
580 bdb_unlocked_cache_return_entry_w(&bdb
->bi_cache
, e
);
587 if ( opinfo
.boi_oe
.oe_key
) {
588 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
591 send_ldap_result( op
, rs
);
592 slap_graduate_commit_csn( op
);
594 if( preread_ctrl
!= NULL
&& (*preread_ctrl
) != NULL
) {
595 slap_sl_free( (*preread_ctrl
)->ldctl_value
.bv_val
, op
->o_tmpmemctx
);
596 slap_sl_free( *preread_ctrl
, op
->o_tmpmemctx
);
599 if( rs
->sr_err
== LDAP_SUCCESS
&& bdb
->bi_txn_cp_kbyte
) {
600 TXN_CHECKPOINT( bdb
->bi_dbenv
,
601 bdb
->bi_txn_cp_kbyte
, bdb
->bi_txn_cp_min
, 0 );