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-2011 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 "lutil.h"
23#include "back-bdb.h"
24
25int
26bdb_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 = {{{ 0 }}};
39	ID	eid;
40
41	DB_LOCK		lock, plock;
42
43	int		num_retries = 0;
44
45	int     rc;
46
47	LDAPControl **preread_ctrl = NULL;
48	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
49	int num_ctrls = 0;
50
51	int	parent_is_glue = 0;
52	int parent_is_leaf = 0;
53
54#ifdef LDAP_X_TXN
55	int settle = 0;
56#endif
57
58	Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_delete) ": %s\n",
59		op->o_req_dn.bv_val, 0, 0 );
60
61#ifdef LDAP_X_TXN
62	if( op->o_txnSpec ) {
63		/* acquire connection lock */
64		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
65		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
66			rs->sr_text = "invalid transaction identifier";
67			rs->sr_err = LDAP_X_TXN_ID_INVALID;
68			goto txnReturn;
69		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
70			settle=1;
71			goto txnReturn;
72		}
73
74		if( op->o_conn->c_txn_backend == NULL ) {
75			op->o_conn->c_txn_backend = op->o_bd;
76
77		} else if( op->o_conn->c_txn_backend != op->o_bd ) {
78			rs->sr_text = "transaction cannot span multiple database contexts";
79			rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
80			goto txnReturn;
81		}
82
83		/* insert operation into transaction */
84
85		rs->sr_text = "transaction specified";
86		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
87
88txnReturn:
89		/* release connection lock */
90		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
91
92		if( !settle ) {
93			send_ldap_result( op, rs );
94			return rs->sr_err;
95		}
96	}
97#endif
98
99	ctrls[num_ctrls] = 0;
100
101	/* allocate CSN */
102	if ( BER_BVISNULL( &op->o_csn ) ) {
103		struct berval csn;
104		char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
105
106		csn.bv_val = csnbuf;
107		csn.bv_len = sizeof(csnbuf);
108		slap_get_csn( op, &csn, 1 );
109	}
110
111	if( 0 ) {
112retry:	/* transaction retry */
113		if( e != NULL ) {
114			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
115			e = NULL;
116		}
117		if( p != NULL ) {
118			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
119			p = NULL;
120		}
121		Debug( LDAP_DEBUG_TRACE,
122			"==> " LDAP_XSTRING(bdb_delete) ": retrying...\n",
123			0, 0, 0 );
124		rs->sr_err = TXN_ABORT( ltid );
125		ltid = NULL;
126		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
127		opinfo.boi_oe.oe_key = NULL;
128		op->o_do_not_cache = opinfo.boi_acl_cache;
129		if( rs->sr_err != 0 ) {
130			rs->sr_err = LDAP_OTHER;
131			rs->sr_text = "internal error";
132			goto return_results;
133		}
134		if ( op->o_abandon ) {
135			rs->sr_err = SLAPD_ABANDON;
136			goto return_results;
137		}
138		parent_is_glue = 0;
139		parent_is_leaf = 0;
140		bdb_trans_backoff( ++num_retries );
141	}
142
143	/* begin transaction */
144	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid,
145		bdb->bi_db_opflags );
146	rs->sr_text = NULL;
147	if( rs->sr_err != 0 ) {
148		Debug( LDAP_DEBUG_TRACE,
149			LDAP_XSTRING(bdb_delete) ": txn_begin failed: "
150			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
151		rs->sr_err = LDAP_OTHER;
152		rs->sr_text = "internal error";
153		goto return_results;
154	}
155
156	opinfo.boi_oe.oe_key = bdb;
157	opinfo.boi_txn = ltid;
158	opinfo.boi_err = 0;
159	opinfo.boi_acl_cache = op->o_do_not_cache;
160	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
161
162	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
163		dnParent( &op->o_req_ndn, &pdn );
164	}
165
166	/* get entry */
167	rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
168		&lock );
169
170	switch( rs->sr_err ) {
171	case 0:
172	case DB_NOTFOUND:
173		break;
174	case DB_LOCK_DEADLOCK:
175	case DB_LOCK_NOTGRANTED:
176		goto retry;
177	case LDAP_BUSY:
178		rs->sr_text = "ldap server busy";
179		goto return_results;
180	default:
181		rs->sr_err = LDAP_OTHER;
182		rs->sr_text = "internal error";
183		goto return_results;
184	}
185
186	if ( rs->sr_err == 0 ) {
187		e = ei->bei_e;
188		eip = ei->bei_parent;
189	} else {
190		matched = ei->bei_e;
191	}
192
193	/* FIXME : dn2entry() should return non-glue entry */
194	if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
195		Debug( LDAP_DEBUG_ARGS,
196			"<=- " LDAP_XSTRING(bdb_delete) ": no such object %s\n",
197			op->o_req_dn.bv_val, 0, 0);
198
199		if ( matched != NULL ) {
200			rs->sr_matched = ch_strdup( matched->e_dn );
201			rs->sr_ref = is_entry_referral( matched )
202				? get_entry_referrals( op, matched )
203				: NULL;
204			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
205			matched = NULL;
206
207		} else {
208			rs->sr_ref = referral_rewrite( default_referral, NULL,
209					&op->o_req_dn, LDAP_SCOPE_DEFAULT );
210		}
211
212		rs->sr_err = LDAP_REFERRAL;
213		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
214		goto return_results;
215	}
216
217	rc = bdb_cache_find_id( op, ltid, eip->bei_id, &eip, 0, &plock );
218	switch( rc ) {
219	case DB_LOCK_DEADLOCK:
220	case DB_LOCK_NOTGRANTED:
221		goto retry;
222	case 0:
223	case DB_NOTFOUND:
224		break;
225	default:
226		rs->sr_err = LDAP_OTHER;
227		rs->sr_text = "internal error";
228		goto return_results;
229	}
230	if ( eip ) p = eip->bei_e;
231
232	if ( pdn.bv_len != 0 ) {
233		if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
234			Debug( LDAP_DEBUG_TRACE,
235				"<=- " LDAP_XSTRING(bdb_delete) ": parent "
236				"does not exist\n", 0, 0, 0 );
237			rs->sr_err = LDAP_OTHER;
238			rs->sr_text = "could not locate parent of entry";
239			goto return_results;
240		}
241
242		/* check parent for "children" acl */
243		rs->sr_err = access_allowed( op, p,
244			children, NULL, ACL_WDEL, NULL );
245
246		if ( !rs->sr_err  ) {
247			switch( opinfo.boi_err ) {
248			case DB_LOCK_DEADLOCK:
249			case DB_LOCK_NOTGRANTED:
250				goto retry;
251			}
252
253			Debug( LDAP_DEBUG_TRACE,
254				"<=- " LDAP_XSTRING(bdb_delete) ": no write "
255				"access to parent\n", 0, 0, 0 );
256			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
257			rs->sr_text = "no write access to parent";
258			goto return_results;
259		}
260
261	} else {
262		/* no parent, must be root to delete */
263		if( ! be_isroot( op ) ) {
264			if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
265				|| be_shadow_update( op ) ) {
266				p = (Entry *)&slap_entry_root;
267
268				/* check parent for "children" acl */
269				rs->sr_err = access_allowed( op, p,
270					children, NULL, ACL_WDEL, NULL );
271
272				p = NULL;
273
274				if ( !rs->sr_err  ) {
275					switch( opinfo.boi_err ) {
276					case DB_LOCK_DEADLOCK:
277					case DB_LOCK_NOTGRANTED:
278						goto retry;
279					}
280
281					Debug( LDAP_DEBUG_TRACE,
282						"<=- " LDAP_XSTRING(bdb_delete)
283						": no access to parent\n",
284						0, 0, 0 );
285					rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
286					rs->sr_text = "no write access to parent";
287					goto return_results;
288				}
289
290			} else {
291				Debug( LDAP_DEBUG_TRACE,
292					"<=- " LDAP_XSTRING(bdb_delete)
293					": no parent and not root\n", 0, 0, 0 );
294				rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
295				goto return_results;
296			}
297		}
298	}
299
300	if ( get_assert( op ) &&
301		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
302	{
303		rs->sr_err = LDAP_ASSERTION_FAILED;
304		goto return_results;
305	}
306
307	rs->sr_err = access_allowed( op, e,
308		entry, NULL, ACL_WDEL, NULL );
309
310	if ( !rs->sr_err  ) {
311		switch( opinfo.boi_err ) {
312		case DB_LOCK_DEADLOCK:
313		case DB_LOCK_NOTGRANTED:
314			goto retry;
315		}
316
317		Debug( LDAP_DEBUG_TRACE,
318			"<=- " LDAP_XSTRING(bdb_delete) ": no write access "
319			"to entry\n", 0, 0, 0 );
320		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
321		rs->sr_text = "no write access to entry";
322		goto return_results;
323	}
324
325	if ( !manageDSAit && is_entry_referral( e ) ) {
326		/* entry is a referral, don't allow delete */
327		rs->sr_ref = get_entry_referrals( op, e );
328
329		Debug( LDAP_DEBUG_TRACE,
330			LDAP_XSTRING(bdb_delete) ": entry is referral\n",
331			0, 0, 0 );
332
333		rs->sr_err = LDAP_REFERRAL;
334		rs->sr_matched = ch_strdup( e->e_name.bv_val );
335		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
336		goto return_results;
337	}
338
339	/* pre-read */
340	if( op->o_preread ) {
341		if( preread_ctrl == NULL ) {
342			preread_ctrl = &ctrls[num_ctrls++];
343			ctrls[num_ctrls] = NULL;
344		}
345		if( slap_read_controls( op, rs, e,
346			&slap_pre_read_bv, preread_ctrl ) )
347		{
348			Debug( LDAP_DEBUG_TRACE,
349				"<=- " LDAP_XSTRING(bdb_delete) ": pre-read "
350				"failed!\n", 0, 0, 0 );
351			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
352				/* FIXME: is it correct to abort
353				 * operation if control fails? */
354				goto return_results;
355			}
356		}
357	}
358
359	/* nested transaction */
360	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2,
361		bdb->bi_db_opflags );
362	rs->sr_text = NULL;
363	if( rs->sr_err != 0 ) {
364		Debug( LDAP_DEBUG_TRACE,
365			LDAP_XSTRING(bdb_delete) ": txn_begin(2) failed: "
366			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
367		rs->sr_err = LDAP_OTHER;
368		rs->sr_text = "internal error";
369		goto return_results;
370	}
371
372	BDB_LOG_PRINTF( bdb->bi_dbenv, lt2, "slapd Starting delete %s(%d)",
373		e->e_nname.bv_val, e->e_id );
374
375	/* Can't do it if we have kids */
376	rs->sr_err = bdb_cache_children( op, lt2, e );
377	if( rs->sr_err != DB_NOTFOUND ) {
378		switch( rs->sr_err ) {
379		case DB_LOCK_DEADLOCK:
380		case DB_LOCK_NOTGRANTED:
381			goto retry;
382		case 0:
383			Debug(LDAP_DEBUG_ARGS,
384				"<=- " LDAP_XSTRING(bdb_delete)
385				": non-leaf %s\n",
386				op->o_req_dn.bv_val, 0, 0);
387			rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
388			rs->sr_text = "subordinate objects must be deleted first";
389			break;
390		default:
391			Debug(LDAP_DEBUG_ARGS,
392				"<=- " LDAP_XSTRING(bdb_delete)
393				": has_children failed: %s (%d)\n",
394				db_strerror(rs->sr_err), rs->sr_err, 0 );
395			rs->sr_err = LDAP_OTHER;
396			rs->sr_text = "internal error";
397		}
398		goto return_results;
399	}
400
401	/* delete from dn2id */
402	rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
403	if ( rs->sr_err != 0 ) {
404		Debug(LDAP_DEBUG_TRACE,
405			"<=- " LDAP_XSTRING(bdb_delete) ": dn2id failed: "
406			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
407		switch( rs->sr_err ) {
408		case DB_LOCK_DEADLOCK:
409		case DB_LOCK_NOTGRANTED:
410			goto retry;
411		}
412		rs->sr_text = "DN index delete failed";
413		rs->sr_err = LDAP_OTHER;
414		goto return_results;
415	}
416
417	/* delete indices for old attributes */
418	rs->sr_err = bdb_index_entry_del( op, lt2, e );
419	if ( rs->sr_err != LDAP_SUCCESS ) {
420		Debug(LDAP_DEBUG_TRACE,
421			"<=- " LDAP_XSTRING(bdb_delete) ": index failed: "
422			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
423		switch( rs->sr_err ) {
424		case DB_LOCK_DEADLOCK:
425		case DB_LOCK_NOTGRANTED:
426			goto retry;
427		}
428		rs->sr_text = "entry index delete failed";
429		rs->sr_err = LDAP_OTHER;
430		goto return_results;
431	}
432
433	/* fixup delete CSN */
434	if ( !SLAP_SHADOW( op->o_bd )) {
435		struct berval vals[2];
436
437		assert( !BER_BVISNULL( &op->o_csn ) );
438		vals[0] = op->o_csn;
439		BER_BVZERO( &vals[1] );
440		rs->sr_err = bdb_index_values( op, lt2, slap_schema.si_ad_entryCSN,
441			vals, 0, SLAP_INDEX_ADD_OP );
442	if ( rs->sr_err != LDAP_SUCCESS ) {
443			switch( rs->sr_err ) {
444			case DB_LOCK_DEADLOCK:
445			case DB_LOCK_NOTGRANTED:
446				goto retry;
447			}
448			rs->sr_text = "entryCSN index update failed";
449			rs->sr_err = LDAP_OTHER;
450			goto return_results;
451		}
452	}
453
454	/* delete from id2entry */
455	rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
456	if ( rs->sr_err != 0 ) {
457		Debug( LDAP_DEBUG_TRACE,
458			"<=- " LDAP_XSTRING(bdb_delete) ": id2entry failed: "
459			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
460		switch( rs->sr_err ) {
461		case DB_LOCK_DEADLOCK:
462		case DB_LOCK_NOTGRANTED:
463			goto retry;
464		}
465		rs->sr_text = "entry delete failed";
466		rs->sr_err = LDAP_OTHER;
467		goto return_results;
468	}
469
470	if ( pdn.bv_len != 0 ) {
471		parent_is_glue = is_entry_glue(p);
472		rs->sr_err = bdb_cache_children( op, lt2, p );
473		if ( rs->sr_err != DB_NOTFOUND ) {
474			switch( rs->sr_err ) {
475			case DB_LOCK_DEADLOCK:
476			case DB_LOCK_NOTGRANTED:
477				goto retry;
478			case 0:
479				break;
480			default:
481				Debug(LDAP_DEBUG_ARGS,
482					"<=- " LDAP_XSTRING(bdb_delete)
483					": has_children failed: %s (%d)\n",
484					db_strerror(rs->sr_err), rs->sr_err, 0 );
485				rs->sr_err = LDAP_OTHER;
486				rs->sr_text = "internal error";
487				goto return_results;
488			}
489			parent_is_leaf = 1;
490		}
491		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
492		p = NULL;
493	}
494
495	BDB_LOG_PRINTF( bdb->bi_dbenv, lt2, "slapd Commit1 delete %s(%d)",
496		e->e_nname.bv_val, e->e_id );
497
498	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
499		rs->sr_err = LDAP_OTHER;
500		rs->sr_text = "txn_commit(2) failed";
501		goto return_results;
502	}
503
504	eid = e->e_id;
505
506#if 0	/* Do we want to reclaim deleted IDs? */
507	ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
508	if ( e->e_id == bdb->bi_lastid ) {
509		bdb_last_id( op->o_bd, ltid );
510	}
511	ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
512#endif
513
514	if( op->o_noop ) {
515		if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
516			rs->sr_text = "txn_abort (no-op) failed";
517		} else {
518			rs->sr_err = LDAP_X_NO_OPERATION;
519			ltid = NULL;
520			goto return_results;
521		}
522	} else {
523
524		BDB_LOG_PRINTF( bdb->bi_dbenv, ltid, "slapd Cache delete %s(%d)",
525			e->e_nname.bv_val, e->e_id );
526
527		rc = bdb_cache_delete( bdb, e, ltid, &lock );
528		switch( rc ) {
529		case DB_LOCK_DEADLOCK:
530		case DB_LOCK_NOTGRANTED:
531			goto retry;
532		}
533
534		rs->sr_err = TXN_COMMIT( ltid, 0 );
535	}
536	ltid = NULL;
537	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
538	opinfo.boi_oe.oe_key = NULL;
539
540	BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Committed delete %s(%d)",
541		e->e_nname.bv_val, e->e_id );
542
543	if( rs->sr_err != 0 ) {
544		Debug( LDAP_DEBUG_TRACE,
545			LDAP_XSTRING(bdb_delete) ": txn_%s failed: %s (%d)\n",
546			op->o_noop ? "abort (no-op)" : "commit",
547			db_strerror(rs->sr_err), rs->sr_err );
548		rs->sr_err = LDAP_OTHER;
549		rs->sr_text = "commit failed";
550
551		goto return_results;
552	}
553
554	Debug( LDAP_DEBUG_TRACE,
555		LDAP_XSTRING(bdb_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
556		op->o_noop ? " (no-op)" : "",
557		eid, op->o_req_dn.bv_val );
558	rs->sr_err = LDAP_SUCCESS;
559	rs->sr_text = NULL;
560	if( num_ctrls ) rs->sr_ctrls = ctrls;
561
562return_results:
563	if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
564		op->o_delete_glue_parent = 1;
565	}
566
567	if ( p )
568		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
569
570	/* free entry */
571	if( e != NULL ) {
572		if ( rs->sr_err == LDAP_SUCCESS ) {
573			/* Free the EntryInfo and the Entry */
574			bdb_cache_entryinfo_lock( BEI(e) );
575			bdb_cache_delete_cleanup( &bdb->bi_cache, BEI(e) );
576		} else {
577			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
578		}
579	}
580
581	if( ltid != NULL ) {
582		TXN_ABORT( ltid );
583	}
584	if ( opinfo.boi_oe.oe_key ) {
585		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
586	}
587
588	send_ldap_result( op, rs );
589	slap_graduate_commit_csn( op );
590
591	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
592		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
593		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
594	}
595
596	if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp_kbyte ) {
597		TXN_CHECKPOINT( bdb->bi_dbenv,
598			bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
599	}
600	return rs->sr_err;
601}
602